create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / util / LinearInterpolator.java
1 package net.sf.openrocket.util;
2
3 import java.util.Iterator;
4 import java.util.List;
5 import java.util.SortedMap;
6 import java.util.TreeMap;
7
8 public class LinearInterpolator implements Cloneable {
9
10         private TreeMap<Double, Double> sortMap = new TreeMap<Double,Double>();
11
12         /**
13          * Construct a <code>LinearInterpolator</code> with no points.  Some points must be
14          * added using {@link #addPoints(double[], double[])} before using the interpolator.
15          */
16         public LinearInterpolator() {
17         }
18
19         /**
20          * Construct a <code>LinearInterpolator</code> with the given points.
21          * 
22          * @param x             the x-coordinates of the points.
23          * @param y             the y-coordinates of the points.
24          * @throws IllegalArgumentException             if the lengths of <code>x</code> and <code>y</code>
25          *                                                                              are not equal.
26          * @see #addPoints(double[], double[])
27          */
28         public LinearInterpolator(double[] x, double[] y) {
29                 addPoints(x,y);
30         }
31
32         public LinearInterpolator(List<Double> x, List<Double> y) {
33                 addPoints(x,y);
34         }
35
36         /**
37          * Add the point to the linear interpolation.
38          * 
39          * @param x             the x-coordinate of the point.
40          * @param y             the y-coordinate of the point.
41          */
42         public void addPoint(double x, double y) {
43                 sortMap.put(x, y);
44         }
45
46         /**
47          * Add the points to the linear interpolation.
48          * 
49          * @param x             the x-coordinates of the points.
50          * @param y             the y-coordinates of the points.
51          * @throws IllegalArgumentException             if the lengths of <code>x</code> and <code>y</code>
52          *                                                                              are not equal.
53          */
54         public void addPoints(double[] x, double[] y) {
55                 if (x.length != y.length) {
56                         throw new IllegalArgumentException("Array lengths do not match, x="+x.length +
57                                         " y="+y.length);
58                 }
59                 for (int i=0; i < x.length; i++) {
60                         sortMap.put(x[i],y[i]);
61                 }
62         }
63
64         public void addPoints(List<Double> x, List<Double> y){
65                 if (x.size() != y.size()) {
66                         throw new IllegalArgumentException("Array lengths do not match, x="+x.size() +
67                                         " y="+y.size());
68                 }
69                 for (int i=0; i < x.size(); i++) {
70                         sortMap.put( (Double) x.toArray()[i], (Double) y.toArray()[i]);
71                 }
72         }
73
74
75         public double getValue(double x) {
76                 double x1, x2;
77                 Double y1, y2;
78                 // Froyo does not support floorEntry, firstEntry or higherEntry.  We instead have to
79                 // resort to using other more awkward methods.
80
81                 y1 = sortMap.get(x);
82
83                 if ( y1 != null ) {
84                         // Wow, x was a key in the map.  Such luck.
85                         return y1.doubleValue();
86                 }
87
88                 // we now know that x is not in the map, so we need to find the lower and higher keys.
89                 
90                 // let's just make certain that our map is not empty.
91                 if ( sortMap.isEmpty() ) {
92                         throw new IllegalStateException("No points added yet to the interpolator.");
93                 }
94                 
95                 // firstKey in the map - cannot be null since the map is not empty.
96                 Double firstKey = sortMap.firstKey();
97
98                 // x is smaller than the first entry in the map.
99                 if ( x < firstKey.doubleValue() ) {
100                         y1 = sortMap.get(firstKey);
101                         return y1.doubleValue();
102                 }
103                 
104                 // floor key is the largest key smaller than x - since we have at least one key,
105                 // and x>=firstKey, we know that floorKey != null.
106                 Double floorKey = sortMap.subMap(firstKey, x).lastKey();
107
108                 x1 = floorKey.doubleValue();
109                 y1 = sortMap.get(floorKey);
110
111                 // Now we need to find the key that is greater or equal to x
112                 SortedMap<Double,Double> tailMap = sortMap.tailMap(x);
113
114                 // Check if x is bigger than all the entries.
115                 if ( tailMap.isEmpty() ) {
116                         return y1.doubleValue();
117                 }
118                 Double ceilKey = tailMap.firstKey();
119                 
120                 // Check if x is bigger than all the entries.
121                 if ( ceilKey == null ) {
122                         return y1.doubleValue();
123                 }
124                 
125                 x2 = ceilKey.doubleValue();
126                 y2 = sortMap.get(ceilKey);
127
128                 return (x - x1)/(x2-x1) * (y2-y1) + y1;
129         }
130
131
132         public double[] getXPoints() {
133                 double[] x = new double[sortMap.size()];
134                 Iterator<Double> iter = sortMap.keySet().iterator();
135                 for (int i=0; iter.hasNext(); i++) {
136                         x[i] = iter.next();
137                 }
138                 return x;
139         }
140
141
142         @SuppressWarnings("unchecked")
143         @Override
144         public LinearInterpolator clone() {
145                 try {
146                         LinearInterpolator other = (LinearInterpolator)super.clone();
147                         other.sortMap = (TreeMap<Double,Double>)this.sortMap.clone();
148                         return other;
149                 } catch (CloneNotSupportedException e) {
150                         throw new BugException("CloneNotSupportedException?!",e);
151                 }
152         }
153
154 }