PiecewiseLerp for cheaply interpolating between cached results of more complex interpolation functions, based on https://www.alanzucconi.com/2021/01/24/piecewise-interpolation/
// An additional implementation for piecewise interpolation described in this article: | |
// https://www.alanzucconi.com/2021/01/24/linear-interpolation/ | |
// It has the limitation that it requires the delta difference between 2 adjacent values of the input array to always be constant. | |
// For example: inputs[2] - inputs[1] = 0.2f; inputs inputs[3] - inputs[2] = 0.2f; etc. | |
public static float PiecewiseLerp (float[] inputs, float[] results, float desiredInput) | |
{ | |
int n = inputs.Length; | |
float inputMin = inputs[0]; | |
float inputMax = inputs[n - 1]; | |
// Don't support extrapolation: | |
if (desiredInput < inputMin) | |
return results[0]; | |
if (desiredInput >= inputMax) | |
return results[n - 1]; | |
// Map to values that correspond to the array index progression | |
float percent = Mathf.InverseLerp(inputMin, inputMax, desiredInput); | |
float step = 1f / (n - 1); | |
int matchedRangeStartIndex = (int) (percent / step); | |
float matchedRangePct = (percent % step) / step; | |
return Mathf.LerpUnclamped(results[matchedRangeStartIndex], results[matchedRangeStartIndex + 1], matchedRangePct); | |
} |
// Demonstration of the PiecewiseLerp function. Can be ran on https://dotnetfiddle.net/ | |
using System; | |
public class Program | |
{ | |
public static void Main() | |
{ | |
TestPiecewiseLerp(); | |
} | |
private static void TestPiecewiseLerp() | |
{ | |
float[] ranges = new float[]{ 0.05f, 0.1f, 0.15f, 0.2f, 0.25f}; | |
float[] results = new float[] { 2.05f, 2.1f, 2.15f, 2.2f, 2.25f }; | |
float[] interpolators = new float[] { -1f, 0f, 0.05f, 0.1f, 0.12f, 0.2f, 0.25f, 0.5f }; | |
foreach (float interpolator in interpolators) | |
{ | |
Console.WriteLine(string.Format("Result for {0} is {1}", interpolator, PiecewiseLerp(ranges, results, interpolator))); | |
} | |
} | |
private static float PiecewiseLerp (float[] inputs, float[] results, float desiredInput) | |
{ | |
int n = inputs.Length; | |
float inputMin = inputs[0]; | |
float inputMax = inputs[n - 1]; | |
// Don't support extrapolation: | |
if (desiredInput < inputMin) | |
return results[0]; | |
if (desiredInput >= inputMax) | |
return results[n - 1]; | |
// Map to values that correspond to the array index progression | |
float percent = Mathf.InverseLerp(inputMin, inputMax, desiredInput); | |
float step = 1f / (n - 1); | |
int matchedRangeStartIndex = (int) (percent / step); | |
float matchedRangePct = (percent % step) / step; | |
return Mathf.LerpUnclamped(results[matchedRangeStartIndex], results[matchedRangeStartIndex + 1], matchedRangePct); | |
} | |
} | |
public static class Mathf | |
{ | |
public static float LerpUnclamped(float min, float max, float x) | |
{ | |
return min + (max - min) * x; | |
} | |
public static float InverseLerp(float min, float max, float value) | |
{ | |
if (min == max) | |
return min; | |
return (value - min) / (max - min); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment