001/* An interpolator for a specified array of indexes and values. 002 003 Copyright (c) 1998-2013 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.math; 029 030/////////////////////////////////////////////////////////////////// 031//// Interpolation 032 033/** 034 This class provides algorithms to do interpolation. Currently, zero, 035 first, and third order interpolations are supported. These are the 036 interpolation orders most often used in practice. zero order interpolation 037 holds the last reference value; first order does linear interpolation; 038 and third order interpolation is based on the Hermite curves in chapter 039 11 of "Computer Graphic, Principles and Practice", by Foley, van Dam, Feiner 040 and Hughes, 2nd ed. in C, 1996. 041 <p> 042 The setValues() method specifies the reference values as a double 043 array. setIndexes() specifies the indexes of those values as an 044 int array. These two arrays must have the same length, and the indexes 045 must be increasing and non-negative; otherwise an exception will be thrown. 046 The values are periodic if a positive period is set by setPeriod(). In 047 this case, the period must be greater than the largest index, and 048 values within the index range 0 to (period-1) are repeated indefinitely. 049 If the period is zero, the values are not periodic, and the values 050 outside the range of the indexes are considered to be 0.0. 051 The interpolation order is set by setOrder(). 052 <p> 053 The default reference values are {1.0, 0.0} and the indexes are {0, 1}. 054 The default period is 2 and the order is 0. 055 <p> 056 057 @author Sarah Packman, Yuhong Xiong 058 @version $Id$ 059 @since Ptolemy II 0.4 060 @Pt.ProposedRating Yellow (yuhong) 061 @Pt.AcceptedRating red (cxh) 062 */ 063public class Interpolation { 064 /** Construct an instance of Interpolation using the default parameters. 065 */ 066 public Interpolation() { 067 } 068 069 /////////////////////////////////////////////////////////////////// 070 //// public methods //// 071 072 /** Return the reference indexes. 073 * @return An int array. 074 * @see #setIndexes(int[]) 075 */ 076 public int[] getIndexes() { 077 return _indexes; 078 } 079 080 /** Return the interpolation order. 081 * @return An int. 082 * @see #setOrder(int) 083 */ 084 public int getOrder() { 085 return _order; 086 } 087 088 /** Return the value repetition period. 089 * @return An int. 090 * @see #setPeriod(int) 091 */ 092 public int getPeriod() { 093 return _period; 094 } 095 096 /** Return the reference values. 097 * @return An double array. 098 * @see #setValues(double[]) 099 */ 100 public double[] getValues() { 101 return _values; 102 } 103 104 /** Return the interpolation result for the specified index. 105 * @param index The point of interpolation. Can be negative 106 * @return A double. 107 * @exception IllegalStateException If the index and value arrays 108 * do not have the same length, or the period is not 0 and not 109 * greater than the largest index. 110 */ 111 public double interpolate(int index) { 112 int numRefPoints = _indexes.length; 113 114 if (numRefPoints != _values.length) { 115 throw new IllegalStateException("Interpolation.interpolate(): " 116 + "The index and value arrays do " 117 + "not have the same length."); 118 } 119 120 int largestIndex = _indexes[numRefPoints - 1]; 121 122 if (_period != 0 && _period <= largestIndex) { 123 throw new IllegalStateException("Interpolation.interpolate(): " 124 + "The period is not 0 and not " + "greater than the " 125 + "largest index."); 126 } 127 128 if (index < 0 || index > largestIndex) { 129 if (_period == 0) { 130 return 0.0; 131 } else { 132 // convert index to a value within [0, period-1] 133 if (index < 0) { 134 index += (-index / _period + 1) * _period; 135 } 136 137 index %= _period; 138 } 139 } 140 141 // index is now within [0, period-1]. If it is outside the range of 142 // the smallest and the largest index, values must be periodic. 143 // Handle a special case where the number of reference points is 144 // 1. The code for order 3 later won't work for this case. 145 if (numRefPoints == 1) { 146 return _values[0]; 147 } 148 149 // indexIndexStart is the index to _indexes whose entry is the 150 // index to the left of the interpolation point. 151 int indexIndexStart = -1; 152 153 // search though all indexes to find iStart. 154 for (int i = 0; i < numRefPoints; i++) { 155 if (_indexes[i] == index) { 156 return _values[i]; 157 } else if (_indexes[i] < index) { 158 indexIndexStart = i; 159 } else { 160 break; 161 } 162 } 163 164 // Perform interpolation 165 if (_order == 0) { 166 if (indexIndexStart != -1) { 167 return _values[indexIndexStart]; 168 } else { 169 return _values[numRefPoints - 1]; 170 } 171 } 172 173 // order must be 1 or 3, need at least the two points surrounding 174 // the interpolation point. 175 int iStart; 176 177 // order must be 1 or 3, need at least the two points surrounding 178 // the interpolation point. 179 int iEnd; 180 double vStart; 181 double vEnd; 182 183 if (indexIndexStart == -1) { 184 iStart = _indexes[numRefPoints - 1] - _period; 185 vStart = _values[numRefPoints - 1]; 186 } else { 187 iStart = _indexes[indexIndexStart]; 188 vStart = _values[indexIndexStart]; 189 } 190 191 if (indexIndexStart == numRefPoints - 1) { 192 iEnd = _indexes[0] + _period; 193 vEnd = _values[0]; 194 } else { 195 iEnd = _indexes[indexIndexStart + 1]; 196 vEnd = _values[indexIndexStart + 1]; 197 } 198 199 if (_order == 1) { 200 return vStart 201 + (index - iStart) * (vEnd - vStart) / (iEnd - iStart); 202 } 203 204 // order is 3. Need the points before Start and the point after End 205 // to compute the tangent at Start and End. 206 int iBeforeStart; 207 208 // order is 3. Need the points before Start and the point after End 209 // to compute the tangent at Start and End. 210 int iAfterEnd; 211 double vBeforeStart; 212 double vAfterEnd; 213 214 if (indexIndexStart == -1) { 215 iBeforeStart = _indexes[numRefPoints - 2] - _period; 216 vBeforeStart = _values[numRefPoints - 2]; 217 } else if (indexIndexStart == 0) { 218 if (_period > 0) { 219 iBeforeStart = _indexes[numRefPoints - 1] - _period; 220 vBeforeStart = _values[numRefPoints - 1]; 221 } else { 222 // Not periodic 223 iBeforeStart = _indexes[0] - 1; 224 vBeforeStart = 0.0; 225 } 226 } else { 227 iBeforeStart = _indexes[indexIndexStart - 1]; 228 vBeforeStart = _values[indexIndexStart - 1]; 229 } 230 231 if (indexIndexStart == numRefPoints - 1) { 232 iAfterEnd = _indexes[1] + _period; 233 vAfterEnd = _values[1]; 234 } else if (indexIndexStart == numRefPoints - 2) { 235 if (_period > 0) { 236 iAfterEnd = _indexes[0] + _period; 237 vAfterEnd = _values[0]; 238 } else { 239 // Not periodic 240 iAfterEnd = _indexes[numRefPoints - 1] + 1; 241 vAfterEnd = 0.0; 242 } 243 } else { 244 iAfterEnd = _indexes[indexIndexStart + 2]; 245 vAfterEnd = _values[indexIndexStart + 2]; 246 } 247 248 // computer the tangent at Start and End. 249 double tanBefore2Start = (vStart - vBeforeStart) 250 / (iStart - iBeforeStart); 251 double tanStart2End = (vEnd - vStart) / (iEnd - iStart); 252 double tanEnd2After = (vAfterEnd - vEnd) / (iAfterEnd - iEnd); 253 254 double tanStart = 0.5 * (tanBefore2Start + tanStart2End); 255 double tanEnd = 0.5 * (tanStart2End + tanEnd2After); 256 257 return _hermite(index, iStart, vStart, tanStart, iEnd, vEnd, tanEnd); 258 } 259 260 /** Set the reference indexes. 261 * @param indexes An int array. 262 * @exception IllegalArgumentException If the argument array is 263 * not increasing and non-negative. 264 * @see #getIndexes() 265 */ 266 public void setIndexes(int[] indexes) { 267 int prev = -1; 268 269 for (int indexe : indexes) { 270 if (indexe <= prev) { 271 throw new IllegalArgumentException("Interpolation.setIndexes" 272 + " index array is not increasing and non-negative."); 273 } 274 275 prev = indexe; 276 } 277 278 _indexes = indexes; 279 } 280 281 /** Set the interpolation order. 282 * @param order An int. 283 * @exception IllegalArgumentException If the order is not 0, 1, or 3. 284 * @see #getOrder() 285 */ 286 public void setOrder(int order) { 287 if (order != 0 && order != 1 && order != 3) { 288 throw new IllegalArgumentException("Interpolation.setOrder: " 289 + "The order " + order + " is not valid."); 290 } 291 292 _order = order; 293 } 294 295 /** Set the value repetition period. 296 * @param period An int. 297 * @exception IllegalArgumentException If the period is negative. 298 * @see #getPeriod() 299 */ 300 public void setPeriod(int period) { 301 if (period < 0) { 302 throw new IllegalArgumentException( 303 "Interpolation.setPeriod: " + "The period is negative."); 304 } 305 306 _period = period; 307 } 308 309 /** Set the reference values. 310 * @param values A double array. 311 * @see #getValues() 312 */ 313 public void setValues(double[] values) { 314 _values = values; 315 } 316 317 /////////////////////////////////////////////////////////////////// 318 //// private methods //// 319 // Return the Hermite curve interpolation. The arguments are: the 320 // interpolation point index, the index/value/tangent of the starting 321 // reference point, the index/value/tangent of the ending reference 322 // point. 323 private double _hermite(int index, int iStart, double vStart, 324 double tanStart, int iEnd, double vEnd, double tanEnd) { 325 // forming the Hermite matrix M 326 double[][] M = new double[4][4]; 327 double iStartSqr = iStart * iStart; 328 double iEndSqr = iEnd * iEnd; 329 M[0][0] = iStartSqr * iStart; 330 M[0][1] = iStartSqr; 331 M[0][2] = iStart; 332 M[0][3] = 1; 333 334 M[1][0] = iEndSqr * iEnd; 335 M[1][1] = iEndSqr; 336 M[1][2] = iEnd; 337 M[1][3] = 1; 338 339 M[2][0] = 3 * iStartSqr; 340 M[2][1] = 2 * iStart; 341 M[2][2] = 1; 342 M[2][3] = 0; 343 344 M[3][0] = 3 * iEndSqr; 345 M[3][1] = 2 * iEnd; 346 M[3][2] = 1; 347 M[3][3] = 0; 348 349 double[][] MInverse = DoubleMatrixMath.inverse(M); 350 351 // forming the column vector of values and tangents 352 double[] Gh = new double[4]; 353 Gh[0] = vStart; 354 Gh[1] = vEnd; 355 Gh[2] = tanStart; 356 Gh[3] = tanEnd; 357 358 // compute the coefficients vector coef[a, b, c, d] or the 3rd order 359 // curve. 360 double[] coef = DoubleMatrixMath.multiply(Gh, MInverse); 361 362 // compute the interpolated value 363 double indexSqr = index * index; 364 return coef[0] * indexSqr * index + coef[1] * indexSqr + coef[2] * index 365 + coef[3]; 366 } 367 368 /////////////////////////////////////////////////////////////////// 369 //// private variables //// 370 private int[] _indexes = { 0, 1 }; 371 372 private double[] _values = { 1.0, 0.0 }; 373 374 private int _period = 2; 375 376 private int _order = 0; 377}