Skip to content

Instantly share code, notes, and snippets.

@lecho
Created November 24, 2013 14:15
Show Gist options
  • Select an option

  • Save lecho/7627739 to your computer and use it in GitHub Desktop.

Select an option

Save lecho/7627739 to your computer and use it in GitHub Desktop.
Spline interpolation in java.
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package lecho.lib.hellocharts.utils;
import java.util.List;
/**
* Performs spline interpolation given a set of control points.
*
*/
public class SplineInterpolator {
private final List<Float> mX;
private final List<Float> mY;
private final float[] mM;
private SplineInterpolator(List<Float> x, List<Float> y, float[] m) {
mX = x;
mY = y;
mM = m;
}
/**
* Creates a monotone cubic spline from a given set of control points.
*
* The spline is guaranteed to pass through each control point exactly. Moreover, assuming the control points are
* monotonic (Y is non-decreasing or non-increasing) then the interpolated values will also be monotonic.
*
* This function uses the Fritsch-Carlson method for computing the spline parameters.
* http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
*
* @param x
* The X component of the control points, strictly increasing.
* @param y
* The Y component of the control points
* @return
*
* @throws IllegalArgumentException
* if the X or Y arrays are null, have different lengths or have fewer than 2 values.
*/
public static SplineInterpolator createMonotoneCubicSpline(List<Float> x, List<Float> y) {
if (x == null || y == null || x.size() != y.size() || x.size() < 2) {
throw new IllegalArgumentException("There must be at least two control "
+ "points and the arrays must be of equal length.");
}
final int n = x.size();
float[] d = new float[n - 1]; // could optimize this out
float[] m = new float[n];
// Compute slopes of secant lines between successive points.
for (int i = 0; i < n - 1; i++) {
float h = x.get(i + 1) - x.get(i);
if (h <= 0f) {
throw new IllegalArgumentException("The control points must all "
+ "have strictly increasing X values.");
}
d[i] = (y.get(i + 1) - y.get(i)) / h;
}
// Initialize the tangents as the average of the secants.
m[0] = d[0];
for (int i = 1; i < n - 1; i++) {
m[i] = (d[i - 1] + d[i]) * 0.5f;
}
m[n - 1] = d[n - 2];
// Update the tangents to preserve monotonicity.
for (int i = 0; i < n - 1; i++) {
if (d[i] == 0f) { // successive Y values are equal
m[i] = 0f;
m[i + 1] = 0f;
} else {
float a = m[i] / d[i];
float b = m[i + 1] / d[i];
float h = (float) Math.hypot(a, b);
if (h > 9f) {
float t = 3f / h;
m[i] = t * a * d[i];
m[i + 1] = t * b * d[i];
}
}
}
return new SplineInterpolator(x, y, m);
}
/**
* Interpolates the value of Y = f(X) for given X. Clamps X to the domain of the spline.
*
* @param x
* The X value.
* @return The interpolated Y = f(X) value.
*/
public float interpolate(float x) {
// Handle the boundary cases.
final int n = mX.size();
if (Float.isNaN(x)) {
return x;
}
if (x <= mX.get(0)) {
return mY.get(0);
}
if (x >= mX.get(n - 1)) {
return mY.get(n - 1);
}
// Find the index 'i' of the last point with smaller X.
// We know this will be within the spline due to the boundary tests.
int i = 0;
while (x >= mX.get(i + 1)) {
i += 1;
if (x == mX.get(i)) {
return mY.get(i);
}
}
// Perform cubic Hermite spline interpolation.
float h = mX.get(i + 1) - mX.get(i);
float t = (x - mX.get(i)) / h;
return (mY.get(i) * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+ (mY.get(i + 1) * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
}
// For debugging.
@Override
public String toString() {
StringBuilder str = new StringBuilder();
final int n = mX.size();
str.append("[");
for (int i = 0; i < n; i++) {
if (i != 0) {
str.append(", ");
}
str.append("(").append(mX.get(i));
str.append(", ").append(mY.get(i));
str.append(": ").append(mM[i]).append(")");
}
str.append("]");
return str.toString();
}
}
@agdavydov81

Copy link
Copy Markdown

Line 90 Error: Must be "if (h > 3f) {"

@WeeJeek

WeeJeek commented Oct 23, 2021

Copy link
Copy Markdown

what is mM??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment