package net.sf.openrocket.util;
import java.util.Iterator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
public class LinearInterpolator implements Cloneable {
private TreeMap<Double, Double> sortMap = new TreeMap<Double,Double>();
/**
* Construct a <code>LinearInterpolator</code> with no points. Some points must be
* added using {@link #addPoints(double[], double[])} before using the interpolator.
*/
public LinearInterpolator() {
}
/**
* Construct a <code>LinearInterpolator</code> with the given points.
*
* @param x the x-coordinates of the points.
* @param y the y-coordinates of the points.
* @throws IllegalArgumentException if the lengths of <code>x</code> and <code>y</code>
* are not equal.
* @see #addPoints(double[], double[])
*/
public LinearInterpolator(double[] x, double[] y) {
addPoints(x,y);
}
public LinearInterpolator(List<Double> x, List<Double> y) {
addPoints(x,y);
}
/**
* Add the point to the linear interpolation.
*
* @param x the x-coordinate of the point.
* @param y the y-coordinate of the point.
*/
public void addPoint(double x, double y) {
sortMap.put(x, y);
}
/**
* Add the points to the linear interpolation.
*
* @param x the x-coordinates of the points.
* @param y the y-coordinates of the points.
* @throws IllegalArgumentException if the lengths of <code>x</code> and <code>y</code>
* are not equal.
*/
public void addPoints(double[] x, double[] y) {
if (x.length != y.length) {
throw new IllegalArgumentException("Array lengths do not match, x="+x.length +
" y="+y.length);
}
for (int i=0; i < x.length; i++) {
sortMap.put(x[i],y[i]);
}
}
public void addPoints(List<Double> x, List<Double> y){
if (x.size() != y.size()) {
throw new IllegalArgumentException("Array lengths do not match, x="+x.size() +
" y="+y.size());
}
for (int i=0; i < x.size(); i++) {
sortMap.put( (Double) x.toArray()[i], (Double) y.toArray()[i]);
}
}
public double getValue(double x) {
double x1, x2;
Double y1, y2;
// Froyo does not support floorEntry, firstEntry or higherEntry. We instead have to
// resort to using other more awkward methods.
y1 = sortMap.get(x);
if ( y1 != null ) {
// Wow, x was a key in the map. Such luck.
return y1.doubleValue();
}
// we now know that x is not in the map, so we need to find the lower and higher keys.
// let's just make certain that our map is not empty.
if ( sortMap.isEmpty() ) {
throw new IllegalStateException("No points added yet to the interpolator.");
}
// firstKey in the map - cannot be null since the map is not empty.
Double firstKey = sortMap.firstKey();
// x is smaller than the first entry in the map.
if ( x < firstKey.doubleValue() ) {
y1 = sortMap.get(firstKey);
return y1.doubleValue();
}
// floor key is the largest key smaller than x - since we have at least one key,
// and x>=firstKey, we know that floorKey != null.
Double floorKey = sortMap.subMap(firstKey, x).lastKey();
x1 = floorKey.doubleValue();
y1 = sortMap.get(floorKey);
// Now we need to find the key that is greater or equal to x
SortedMap<Double,Double> tailMap = sortMap.tailMap(x);
// Check if x is bigger than all the entries.
if ( tailMap.isEmpty() ) {
return y1.doubleValue();
}
Double ceilKey = tailMap.firstKey();
// Check if x is bigger than all the entries.
if ( ceilKey == null ) {
return y1.doubleValue();
}
x2 = ceilKey.doubleValue();
y2 = sortMap.get(ceilKey);
return (x - x1)/(x2-x1) * (y2-y1) + y1;
}
public double[] getXPoints() {
double[] x = new double[sortMap.size()];
Iterator<Double> iter = sortMap.keySet().iterator();
for (int i=0; iter.hasNext(); i++) {
x[i] = iter.next();
}
return x;
}
@SuppressWarnings("unchecked")
@Override
public LinearInterpolator clone() {
try {
LinearInterpolator other = (LinearInterpolator)super.clone();
other.sortMap = (TreeMap<Double,Double>)this.sortMap.clone();
return other;
} catch (CloneNotSupportedException e) {
throw new BugException("CloneNotSupportedException?!",e);
}
}
}