Project

General

Profile

Download (13.3 KB) Statistics
| Branch: | Revision:

library / src / main / java / org / distorted / library / Interpolator1D.java @ 1e438fc7

1
package org.distorted.library;
2

    
3
import java.util.Vector;
4

    
5
///////////////////////////////////////////////////////////////////////////////////////////////////
6
/** 
7
* A 1-dimensional implementation of the Interpolator class to interpolate between a list 
8
* of Float1Ds.
9
*/
10

    
11
public class Interpolator1D extends Interpolator 
12
  {
13
  
14
///////////////////////////////////////////////////////////////////////////////////////////////////
15
// the coefficients of the X(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
16
// (x) is the vector tangent to the path.
17
// (vx) is the original vector from vv (copied here so when interpolating we can see if it is 
18
// still valid and if not - rebuild the Cache
19
   
20
  private class VectorCache
21
    {
22
    float ax, bx, cx, dx;
23
   
24
    float x;
25
    float vx;
26
    }
27
  
28
  private class VectorNoise
29
    {
30
    float[] nx;
31
   
32
    public VectorNoise()
33
      {
34
      nx = new float[NUM_NOISE]; 
35
      nx[0] = mRnd.nextFloat();
36
      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
37
      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
38
      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
39
      }
40
    }
41
  
42
  private Vector<VectorCache> vc;
43
  private VectorCache tmp1, tmp2;
44
 
45
  private Vector<Float1D> vv;
46
  private Float1D prev, curr, next;
47
 
48
  private Vector<VectorNoise> vn;
49
  private VectorNoise tmpN;
50
  
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52

    
53
  synchronized void createNoise()
54
    {
55
    if( vn==null )
56
      {
57
      vn = new Vector<VectorNoise>();
58
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
59
      }
60
    }
61
  
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63
// no array bounds checking!
64
  
65
  private void vec(int c)
66
    {
67
    int p = c>0 ? c-1: numPoints-1;
68
    int n = c<numPoints-1 ? c+1: 0;
69
    
70
    prev = vv.elementAt(p);
71
    curr = vv.elementAt(c);
72
    next = vv.elementAt(n);
73

    
74
    tmp1 = vc.elementAt(c);
75
    
76
    float px = curr.x - prev.x;
77
    float nx = next.x - curr.x;
78
     
79
    float d = nx*nx;
80
    
81
    if( d>0 )
82
      {
83
      float q = (float)Math.sqrt((px*px)/d);
84
      
85
      if( q>1 )
86
        {
87
        tmp1.x = nx+px/q;
88
        }
89
      else
90
        {
91
        tmp1.x = px+nx*q;
92
        }
93
      }
94
    else
95
      {
96
      tmp1.x = 0.0f;
97
      }
98
    }
99
      
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101
  
102
  private void recomputeCache()
103
    {  
104
    if( numPoints==1 )
105
      {
106
      tmp1= vc.elementAt(0);
107
      curr= vv.elementAt(0);
108
        
109
      tmp1.ax = 0.0f;
110
      tmp1.bx = 0.0f;
111
      tmp1.cx = curr.x;
112
      tmp1.dx = 0.0f;
113
      }
114
    else if( numPoints==2 )
115
      {
116
      tmp1= vc.elementAt(0);
117
      tmp2= vc.elementAt(1);
118
      curr= vv.elementAt(0);
119
      next= vv.elementAt(1);
120
          
121
      tmp1.ax = 0.0f;
122
      tmp1.bx = 0.0f;
123
      tmp1.cx = next.x - curr.x;
124
      tmp1.dx = curr.x;
125
      
126
      tmp2.ax = 0.0f;
127
      tmp2.bx = 0.0f;
128
      tmp2.cx = curr.x - next.x;
129
      tmp2.dx = next.x;
130
      }
131
    else
132
      {
133
      int i, n;  
134
         
135
      for(i=0; i<numPoints; i++) vec(i);
136
   
137
      for(i=0; i<numPoints; i++)
138
        {
139
        n = i<numPoints-1 ? i+1:0;  
140
      
141
        tmp1= vc.elementAt(i);
142
        tmp2= vc.elementAt(n);
143
        curr= vv.elementAt(i);
144
        next= vv.elementAt(n);
145
    
146
        tmp1.vx = curr.x;
147
        
148
        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
149
        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
150
        tmp1.cx = tmp1.x;
151
        tmp1.dx = curr.x;
152
        }
153
      }
154
   
155
    cacheDirty = false;
156
    }
157
  
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159

    
160
  private float noise(float time,int vecNum)
161
    {
162
    float lower, upper, len;  
163
    float d = time*(NUM_NOISE+1);
164
    int index = (int)d;
165
    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
166
    tmpN = vn.elementAt(vecNum);
167
   
168
    if( index==0 )
169
      {
170
      len = 1.0f/(NUM_NOISE+1);  
171
      return (len + mNoise*(tmpN.nx[0]-len))*d;
172
      }
173
    if( index==NUM_NOISE )
174
      {
175
      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
176
      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);   
177
      return (1.0f-lower)*(d-NUM_NOISE) + lower;   
178
      }
179
   
180
    len = ((float)index)/(NUM_NOISE+1);
181
    lower = len + mNoise*(tmpN.nx[index-1]-len);   
182
    len = ((float)index+1)/(NUM_NOISE+1); 
183
    upper = len + mNoise*(tmpN.nx[index  ]-len);
184
            
185
    return (upper-lower)*(d-index) + lower; 
186
    }
187
   
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189
// PUBLIC API
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191
/**
192
 * Default constructor.
193
 */
194
  public Interpolator1D()
195
    {
196
    vv = new Vector<Float1D>();
197
    vc = new Vector<VectorCache>();
198
    vn = null;
199
    numPoints = 0;
200
    cacheDirty = false;
201
    mMode = MODE_LOOP;
202
    mDuration = 0;
203
    mCount = 0.5f;
204
    }
205
  
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207
/**
208
 * Returns the location'th Float1D. 
209
 *   
210
 * @param location the index of the Point we are interested in.
211
 * @return The Float1D, if 0<=location&lt;getNumPoints(), or null otherwise. 
212
 */
213
  public synchronized Float1D getPoint(int location)
214
    {
215
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
216
    }
217
  
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219
/**
220
 * Resets the location'th Point.
221
 * 
222
 * @param location the index of the Point we are setting.
223
 * @param x New value of its first float.
224
 */
225
  public synchronized void setPoint(int location, float x)
226
    {
227
    if( location>=0 && location<numPoints )
228
      {
229
      curr = vv.elementAt(location);
230
   
231
      if( curr!=null )
232
        {
233
        curr.set(x);
234
        cacheDirty=true;
235
        }
236
      }
237
    }
238
 
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
/**
241
 * Adds a new Float1D to the end of our list of Points to interpolate through.
242
 * <p>   
243
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
244
 * here, and later on {@link Float1D#set(float)} it to some new value and the change will
245
 * be seamlessly reflected in the interpolated path.  
246
 * <p>
247
 * A Point can be added multiple times.
248
 *   
249
 * @param v The Point to add.
250
 */
251
  public synchronized void add(Float1D v)
252
    {
253
    if( v!=null )
254
      {
255
      vv.add(v);
256
     
257
      if( vn!=null ) vn.add(new VectorNoise());
258
       
259
      switch(numPoints)
260
        {
261
        case 0: 
262
        case 1: break;
263
        case 2: vc.add(new VectorCache());
264
                vc.add(new VectorCache());
265
                vc.add(new VectorCache());
266
                cacheDirty = true;
267
                break;
268
        default:vc.add(new VectorCache());
269
                cacheDirty = true;
270
        }
271
     
272
      numPoints++;
273
      }
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277
/**
278
 * Adds a new Float1D to the location'th place in our List of Points to interpolate through.  
279
 *   
280
 * @param location Index in our List to add the new Point at.
281
 * @param v The Point to add.
282
 */
283
  public synchronized void add(int location, Float1D v)
284
    {
285
    if( v!=null )
286
      {
287
      vv.add(location, v);
288
      
289
      if( vn!=null ) vn.add(new VectorNoise());
290
             
291
      switch(numPoints)
292
        {
293
        case 0:
294
        case 1: break;
295
        case 2: vc.add(new VectorCache());
296
                vc.add(new VectorCache());
297
                vc.add(new VectorCache());
298
                cacheDirty = true;
299
                break;
300
        default:vc.add(location,new VectorCache());
301
                cacheDirty = true;
302
        }
303
      
304
      numPoints++;
305
      }
306
    }
307
  
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309
/**
310
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
311
 * 
312
 * @param v The Point to remove.
313
 * @return <code>true</code> if we have removed at least one Point.
314
 */
315
  public synchronized boolean remove(Float1D v)
316
    {
317
    int n = vv.indexOf(v);
318
    boolean found = false;
319
   
320
    while( n>=0 ) 
321
      {
322
      vv.remove(n);
323
     
324
      if( vn!=null ) vn.remove(0);
325
     
326
      switch(numPoints)
327
        {
328
        case 0:
329
        case 1:
330
        case 2: break;
331
        case 3: vc.removeAllElements();
332
                break;
333
        default:vc.remove(n);
334
                cacheDirty=true;
335
        }
336

    
337
      numPoints--;
338
      found = true;
339
      n = vv.indexOf(v);
340
      }
341
   
342
    return found;
343
    }
344

    
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346
/**
347
 * Removes a location'th Point from the List of Points we interpolate through.
348
 * 
349
 * @param location index of the Point we want to remove. 
350
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
351
 */
352
  public synchronized boolean remove(int location)
353
    {
354
    if( location>=0 && location<numPoints ) 
355
      {
356
      vv.removeElementAt(location);
357
      
358
      if( vn!=null ) vn.remove(0);
359
     
360
      switch(numPoints)
361
        {
362
        case 0:
363
        case 1: 
364
        case 2: break;
365
        case 3: vc.removeAllElements();
366
                break;
367
        default:vc.removeElementAt(location);
368
        }
369

    
370
      numPoints--;
371
      cacheDirty = true; 
372
      return true;
373
      }
374

    
375
   return false;
376
   }
377
  
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
/**
380
 * Removes all Points.
381
 */
382
  public synchronized void removeAll()
383
    {
384
    numPoints = 0;
385
    vv.removeAllElements();
386
    vc.removeAllElements();
387
    cacheDirty = false;
388
   
389
    if( vn!=null ) vn.removeAllElements();
390
    }
391
 
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393
/**
394
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
395
 * <p>
396
 * Since this is a 1-dimensional Interpolator, the resulting interpolated Float1D gets written
397
 * to a single location in the buffer: buffer[offset]. 
398
 * 
399
 * @param buffer Float buffer we will write the resulting Float1D to.
400
 * @param offset Offset in the buffer where to write the result.
401
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
402
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
403
 */
404
  public synchronized void interpolate(float[] buffer, int offset, float time)
405
    {
406
    switch(numPoints)
407
      {
408
      case 0: buffer[offset] = 0.0f;
409
              break;
410
      case 1: curr = vv.elementAt(0);
411
              buffer[offset] = curr.x;
412
              break;
413
      case 2: curr = vv.elementAt(0);
414
              next = vv.elementAt(1);
415
             
416
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
417
             
418
              if( vn!=null )
419
                {
420
                time = noise(time,0);
421
                }
422
             
423
              buffer[offset] = (next.x-curr.x)*time + curr.x;
424
              break;
425
      default:float t = time;
426
            
427
              switch(mMode)
428
                {
429
                case MODE_LOOP: time = time*numPoints;
430
                                break;
431
                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
432
                                break;
433
                case MODE_JUMP: time = time*(numPoints-1);
434
                                break;
435
                }
436
      
437
              int vecCurr = (int)time;
438
              time = time-vecCurr;
439
      
440
              if( vecCurr>=0 && vecCurr<numPoints )
441
                {
442
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
443
                else if( mVecCurr!= vecCurr )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
444
                  {
445
                  int vecNext;   
446
                  mVecCurr = vecCurr;
447
                                
448
                  switch(mMode)
449
                    {
450
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
451
                                    break;
452
                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
453
                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
454
                                    break;
455
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
456
                                    break;
457
                    default       : vecNext = 0;                
458
                    }
459
              
460
                  next = vv.elementAt(vecNext);
461
                  tmp2 = vc.elementAt(vecNext);
462
              
463
                  if( tmp2.vx!=next.x ) recomputeCache();
464
                  }
465
             
466
                if( vn!=null )
467
                  {
468
                  time = noise(time,vecCurr);
469
                  }
470
            
471
                tmp1 = vc.elementAt(vecCurr);
472
                buffer[offset] = ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
473
                break;
474
                }
475
        }
476
     }  
477
  
478
  }
479
///////////////////////////////////////////////////////////////////////////////////////////////////
480
//
(26-26/30)