Files
2026-02-21 16:45:37 +08:00

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);
}
}
}