221 lines
5.7 KiB
C#
221 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace Moonlit.Core
|
|
{
|
|
public class CubicSpline
|
|
{
|
|
private class TriDiagonalMatrix
|
|
{
|
|
public readonly float[] A;
|
|
|
|
public readonly float[] B;
|
|
|
|
public readonly float[] C;
|
|
|
|
public TriDiagonalMatrix(int n)
|
|
{
|
|
A = new float[n];
|
|
B = new float[n];
|
|
C = new float[n];
|
|
}
|
|
|
|
public float[] Solve(float[] d)
|
|
{
|
|
int num = A.Length;
|
|
if (d.Length != num)
|
|
{
|
|
throw new ArgumentException("The input d is not the same size as this matrix.");
|
|
}
|
|
float[] array = new float[num];
|
|
array[0] = C[0] / B[0];
|
|
for (int i = 1; i < num; i++)
|
|
{
|
|
array[i] = C[i] / (B[i] - array[i - 1] * A[i]);
|
|
}
|
|
float[] array2 = new float[num];
|
|
array2[0] = d[0] / B[0];
|
|
for (int j = 1; j < num; j++)
|
|
{
|
|
array2[j] = (d[j] - array2[j - 1] * A[j]) / (B[j] - array[j - 1] * A[j]);
|
|
}
|
|
float[] array3 = new float[num];
|
|
array3[num - 1] = array2[num - 1];
|
|
for (int num2 = num - 2; num2 >= 0; num2--)
|
|
{
|
|
array3[num2] = array2[num2] - array[num2] * array3[num2 + 1];
|
|
}
|
|
return array3;
|
|
}
|
|
}
|
|
|
|
private float[] _A;
|
|
|
|
private float[] _B;
|
|
|
|
private float[] _XOrig;
|
|
|
|
private float[] _YOrig;
|
|
|
|
private int _LastIndex;
|
|
|
|
public static void Interpolate(float[] x, float[] y, int points, out float[] xs, out float[] ys)
|
|
{
|
|
int num = x.Length;
|
|
float[] array = new float[num];
|
|
array[0] = 0f;
|
|
float num2 = 0f;
|
|
for (int i = 1; i < num; i++)
|
|
{
|
|
float num3 = x[i] - x[i - 1];
|
|
float num4 = y[i] - y[i - 1];
|
|
float num5 = (float)Math.Sqrt(num3 * num3 + num4 * num4);
|
|
num2 = (array[i] = num2 + num5);
|
|
}
|
|
float num6 = num2 / (float)(points - 1);
|
|
float[] array2 = new float[points];
|
|
array2[0] = 0f;
|
|
for (int j = 1; j < points; j++)
|
|
{
|
|
array2[j] = array2[j - 1] + num6;
|
|
}
|
|
CubicSpline cubicSpline = new CubicSpline();
|
|
xs = cubicSpline.FitAndEval(array, x, array2);
|
|
CubicSpline cubicSpline2 = new CubicSpline();
|
|
ys = cubicSpline2.FitAndEval(array, y, array2);
|
|
}
|
|
|
|
public static List<Vector2> Interpolate(List<Vector2> input, int points)
|
|
{
|
|
List<Vector2> list = new List<Vector2>(points);
|
|
float[] x = input.Select((Vector2 p) => p.x).ToArray();
|
|
float[] y = input.Select((Vector2 p) => p.y).ToArray();
|
|
float[] xs;
|
|
float[] ys;
|
|
Interpolate(x, y, points, out xs, out ys);
|
|
for (int num = 0; num < points; num++)
|
|
{
|
|
list.Add(new Vector2(xs[num], ys[num]));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public static List<T> Interpolate<T>(List<T> input, Func<T, Vector2> getter, Action<T, Vector2> setter, int points) where T : class, new()
|
|
{
|
|
List<T> list = new List<T>(points);
|
|
float[] x = input.Select((T p) => getter(p).x).ToArray();
|
|
float[] y = input.Select((T p) => getter(p).y).ToArray();
|
|
float[] xs;
|
|
float[] ys;
|
|
Interpolate(x, y, points, out xs, out ys);
|
|
for (int num = 0; num < points; num++)
|
|
{
|
|
T val = new T();
|
|
setter(val, new Vector2(xs[num], ys[num]));
|
|
list.Add(val);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private float[] FitAndEval(float[] x, float[] y, float[] xs, float startSlope = float.NaN, float endSlope = float.NaN)
|
|
{
|
|
Fit(x, y, startSlope, endSlope);
|
|
return Eval(xs);
|
|
}
|
|
|
|
private void Fit(float[] x, float[] y, float startSlope = float.NaN, float endSlope = float.NaN)
|
|
{
|
|
if (float.IsInfinity(startSlope) || float.IsInfinity(endSlope))
|
|
{
|
|
throw new Exception("startSlope and endSlope cannot be infinity.");
|
|
}
|
|
_XOrig = x;
|
|
_YOrig = y;
|
|
int num = x.Length;
|
|
float[] array = new float[num];
|
|
TriDiagonalMatrix triDiagonalMatrix = new TriDiagonalMatrix(num);
|
|
if (float.IsNaN(startSlope))
|
|
{
|
|
float num2 = x[1] - x[0];
|
|
triDiagonalMatrix.C[0] = 1f / num2;
|
|
triDiagonalMatrix.B[0] = 2f * triDiagonalMatrix.C[0];
|
|
array[0] = 3f * (y[1] - y[0]) / (num2 * num2);
|
|
}
|
|
else
|
|
{
|
|
triDiagonalMatrix.B[0] = 1f;
|
|
array[0] = startSlope;
|
|
}
|
|
for (int i = 1; i < num - 1; i++)
|
|
{
|
|
float num2 = x[i] - x[i - 1];
|
|
float num3 = x[i + 1] - x[i];
|
|
triDiagonalMatrix.A[i] = 1f / num2;
|
|
triDiagonalMatrix.C[i] = 1f / num3;
|
|
triDiagonalMatrix.B[i] = 2f * (triDiagonalMatrix.A[i] + triDiagonalMatrix.C[i]);
|
|
float num4 = y[i] - y[i - 1];
|
|
float num5 = y[i + 1] - y[i];
|
|
array[i] = 3f * (num4 / (num2 * num2) + num5 / (num3 * num3));
|
|
}
|
|
if (float.IsNaN(endSlope))
|
|
{
|
|
float num2 = x[num - 1] - x[num - 2];
|
|
float num4 = y[num - 1] - y[num - 2];
|
|
triDiagonalMatrix.A[num - 1] = 1f / num2;
|
|
triDiagonalMatrix.B[num - 1] = 2f * triDiagonalMatrix.A[num - 1];
|
|
array[num - 1] = 3f * (num4 / (num2 * num2));
|
|
}
|
|
else
|
|
{
|
|
triDiagonalMatrix.B[num - 1] = 1f;
|
|
array[num - 1] = endSlope;
|
|
}
|
|
float[] array2 = triDiagonalMatrix.Solve(array);
|
|
_A = new float[num - 1];
|
|
_B = new float[num - 1];
|
|
for (int j = 1; j < num; j++)
|
|
{
|
|
float num2 = x[j] - x[j - 1];
|
|
float num4 = y[j] - y[j - 1];
|
|
_A[j - 1] = array2[j - 1] * num2 - num4;
|
|
_B[j - 1] = (0f - array2[j]) * num2 + num4;
|
|
}
|
|
}
|
|
|
|
private float[] Eval(float[] x)
|
|
{
|
|
int num = x.Length;
|
|
float[] array = new float[num];
|
|
_LastIndex = 0;
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
int nextXIndex = GetNextXIndex(x[i]);
|
|
array[i] = EvalSpline(x[i], nextXIndex);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
private int GetNextXIndex(float x)
|
|
{
|
|
if (x < _XOrig[_LastIndex])
|
|
{
|
|
throw new ArgumentException("The X values to evaluate must be sorted.");
|
|
}
|
|
while (_LastIndex < _XOrig.Length - 2 && x > _XOrig[_LastIndex + 1])
|
|
{
|
|
_LastIndex++;
|
|
}
|
|
return _LastIndex;
|
|
}
|
|
|
|
private float EvalSpline(float x, int j)
|
|
{
|
|
float num = _XOrig[j + 1] - _XOrig[j];
|
|
float num2 = (x - _XOrig[j]) / num;
|
|
return (1f - num2) * _YOrig[j] + num2 * _YOrig[j + 1] + num2 * (1f - num2) * (_A[j] * (1f - num2) + _B[j] * num2);
|
|
}
|
|
}
|
|
}
|