Project

General

Profile

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

library / src / main / java / org / distorted / library / Interpolator2D.java @ b1e91f2c

1
package org.distorted.library;
2

    
3
import java.util.Vector;
4

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

    
11
public class Interpolator2D extends Interpolator 
12
  {
13

    
14
///////////////////////////////////////////////////////////////////////////////////////////////////
15
// the coefficients of the X(t), Y(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
16
// (x,y) is the vector tangent to the path.
17
// (vx,vy) 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
    float ay, by, cy, dy;
24
   
25
    float x,y;
26
    float vx,vy;
27
    }
28
  
29
  private class VectorNoise
30
    {    
31
    float[] nx;
32
    float[] ny;
33
   
34
    public VectorNoise()
35
      {
36
      nx = new float[NUM_NOISE]; 
37
      nx[0] = mRnd.nextFloat();
38
      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
39
      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
40
      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
41
     
42
      ny = new float[NUM_NOISE];
43
      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
44
      }
45
    }
46
    
47
  private Vector<VectorCache> vc;
48
  private VectorCache tmp1, tmp2;
49
   
50
  private Vector<Float2D> vv;
51
  private Float2D prev, curr, next;
52
 
53
  private Vector<VectorNoise> vn;
54
  private VectorNoise tmpN;
55
  
56
  private float mFactor;
57
 
58
///////////////////////////////////////////////////////////////////////////////////////////////////
59

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

    
81
    tmp1 = vc.elementAt(c);
82
    
83
    float px = curr.x - prev.x;
84
    float py = curr.y - prev.y;
85
    float nx = next.x - curr.x;
86
    float ny = next.y - curr.y;
87
     
88
    float d = nx*nx+ny*ny;
89
    
90
    if( d>0 )
91
      {
92
      float q = (float)Math.sqrt((px*px+py*py)/d);
93
      
94
      if( q>1 )
95
        {
96
        tmp1.x = nx+px/q;
97
        tmp1.y = ny+py/q;
98
        }
99
      else
100
        {
101
        tmp1.x = px+nx*q;
102
        tmp1.y = py+ny*q;
103
        }
104
      }
105
    else
106
      {
107
      tmp1.x = 0.0f;
108
      tmp1.y = 0.0f;
109
      }
110
    }
111
   
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113
  
114
  private void recomputeCache()
115
    {  
116
    if( numPoints==1 )
117
      {
118
      tmp1= vc.elementAt(0);
119
      curr= vv.elementAt(0);
120
              
121
      tmp1.ax = tmp1.ay = 0.0f;
122
      tmp1.bx = tmp1.by = 0.0f;
123
      tmp1.cx = curr.x;
124
      tmp1.cy = curr.y;
125
      tmp1.dx = tmp1.dy = 0.0f;
126
      }
127
    else if( numPoints==2 )
128
      {
129
      tmp1= vc.elementAt(0);
130
      tmp2= vc.elementAt(1);
131
      curr= vv.elementAt(0);
132
      next= vv.elementAt(1);
133
          
134
      tmp1.ax = tmp1.ay = 0.0f;
135
      tmp1.bx = tmp1.by = 0.0f;
136
      tmp1.cx = next.x - curr.x;
137
      tmp1.cy = next.y - curr.y;
138
      tmp1.dx = curr.x;
139
      tmp1.dy = curr.y;
140
      
141
      tmp2.ax = tmp2.ay = 0.0f;
142
      tmp2.bx = tmp2.by = 0.0f;
143
      tmp2.cx = curr.x - next.x;
144
      tmp2.cy = curr.y - next.y;
145
      tmp2.dx = next.x;
146
      tmp2.dy = next.y;
147
      }
148
    else
149
      {
150
      int i, n;  
151
         
152
      for(i=0; i<numPoints; i++) vec(i);
153
   
154
      for(i=0; i<numPoints; i++)
155
        {
156
        n = i<numPoints-1 ? i+1:0;  
157
      
158
        tmp1= vc.elementAt(i);
159
        tmp2= vc.elementAt(n);
160
        curr= vv.elementAt(i);
161
        next= vv.elementAt(n);
162
      
163
        tmp1.vx = curr.x;
164
        tmp1.vy = curr.y;
165
        
166
        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
167
        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
168
        tmp1.cx = tmp1.x;
169
        tmp1.dx = curr.x;
170
      
171
        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
172
        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
173
        tmp1.cy = tmp1.y;
174
        tmp1.dy = curr.y;
175
        }
176
      }
177
    
178
    cacheDirty = false;
179
    }
180
 
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

    
183
  private float noise(float time,int vecNum)
184
    {
185
    float lower, upper, len;  
186
    float d = time*(NUM_NOISE+1);
187
    int index = (int)d;
188
    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
189
    tmpN = vn.elementAt(vecNum);
190
   
191
    float x = d-index;
192
    x = x*x*(3-2*x);
193
   
194
    switch(index)
195
      {
196
      case 0        : mFactor = mNoise*tmpN.ny[0]*x;  
197
                      return time + mNoise*(d*tmpN.nx[0]-time);                
198
      case NUM_NOISE: mFactor= mNoise*tmpN.ny[NUM_NOISE-1]*(1-x);
199
                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
200
                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
201
                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
202
      default       : float yb = tmpN.ny[index  ];
203
                      float ya = tmpN.ny[index-1];
204
                      mFactor  = mNoise*((yb-ya)*x+ya);
205
   
206
                      len = ((float)index)/(NUM_NOISE+1);
207
                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
208
                      len = ((float)index+1)/(NUM_NOISE+1); 
209
                      upper = len + mNoise*(tmpN.nx[index  ]-len);
210
            
211
                      return (upper-lower)*(d-index) + lower; 
212
      }
213
    }
214
  
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216
// PUBLIC API 
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218
/**
219
 * Default constructor.
220
 */
221
  public Interpolator2D()
222
    {
223
    vv = new Vector<Float2D>();
224
    vc = new Vector<VectorCache>();
225
    vn = null;
226
    numPoints = 0;
227
    cacheDirty = false;
228
    mMode = MODE_LOOP;
229
    mDuration = 0;
230
    mCount = 0.5f;
231
    mNoise = 0.0f;
232
    }
233
  
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235
/**
236
 * Returns the location'th Float2D. 
237
 *   
238
 * @param location the index of the Point we are interested in.
239
 * @return The Float2D, if 0<=location&lt;getNumPoints(), or null otherwise. 
240
 */  
241
  public synchronized Float2D getPoint(int location)
242
    {
243
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
244
    }
245
  
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247
/**
248
 * Resets the location'th Point.
249
 * 
250
 * @param location the index of the Point we are setting.
251
 * @param x New value of its first float.
252
 */
253
  public synchronized void setPoint(int location, float x, float y)
254
    {
255
    if( location>=0 && location<numPoints )
256
      {
257
      curr = vv.elementAt(location);
258
   
259
      if( curr!=null )
260
        {
261
        curr.set(x,y);
262
        cacheDirty=true;
263
        }
264
      }
265
    }
266
    
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268
/**
269
 * Adds a new Float2D to the end of our list of Points to interpolate through.
270
 * <p>   
271
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
272
 * here, and later on {@link Float2D#set(float,float)} it to some new value and the change 
273
 * will be seamlessly reflected in the interpolated path.  
274
 * <p>
275
 * A Point can be added multiple times.
276
 *   
277
 * @param v The Point to add.
278
 */  
279
  public synchronized void add(Float2D v)
280
    {
281
    if( v!=null )
282
      {
283
      vv.add(v);
284
     
285
      if( vn!=null ) vn.add(new VectorNoise());
286
       
287
      switch(numPoints)
288
        {
289
        case 0:
290
        case 1: break;
291
        case 2: vc.add(new VectorCache());
292
                vc.add(new VectorCache());
293
                vc.add(new VectorCache());
294
                break;
295
        default:vc.add(new VectorCache());
296
        }
297
     
298
      numPoints++;
299
      cacheDirty = true;
300
      }
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304
/**
305
 * Adds a new Float2D to the location'th place in our List of Points to interpolate through.  
306
 *   
307
 * @param location Index in our List to add the new Point at.
308
 * @param v The Point to add.
309
 */  
310
  public synchronized void add(int location, Float2D v)
311
    {
312
    if( v!=null )
313
      {
314
      vv.add(location, v);
315
      
316
      if( vn!=null ) vn.add(new VectorNoise());
317
      
318
      switch(numPoints)
319
        {
320
        case 0:
321
        case 1: break;
322
        case 2: vc.add(new VectorCache());
323
                vc.add(new VectorCache());
324
                vc.add(new VectorCache());
325
                break;
326
        default:vc.add(location,new VectorCache());
327
        }
328
      
329
      numPoints++;
330
      cacheDirty = true;
331
      }
332
    }
333
  
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335
/**
336
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
337
 * 
338
 * @param v The Point to remove.
339
 * @return <code>true</code> if we have removed at least one Point.
340
 */
341
  public synchronized boolean remove(Float2D v)
342
    {
343
    int n = vv.indexOf(v);
344
    boolean found = false;
345
   
346
    while( n>=0 ) 
347
      {
348
      vv.remove(n);
349
     
350
      if( vn!=null ) vn.remove(0);
351
     
352
      switch(numPoints)
353
        {
354
        case 0:
355
        case 1: 
356
        case 2: break;
357
        case 3: vc.removeAllElements();
358
                break;
359
        default:vc.remove(n);
360
        }
361
     
362
      numPoints--;
363
      found = true;
364
      n = vv.indexOf(v);
365
      }
366
   
367
    if( found ) 
368
      {
369
      cacheDirty=true;
370
      }
371
   
372
    return found;
373
    }
374

    
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376
/**
377
 * Removes a location'th Point from the List of Points we interpolate through.
378
 * 
379
 * @param location index of the Point we want to remove. 
380
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
381
 */
382
  public synchronized boolean remove(int location)
383
    {
384
    if( location>=0 && location<numPoints ) 
385
      {
386
      vv.removeElementAt(location);
387
      
388
      if( vn!=null ) vn.remove(0);
389
      
390
      switch(numPoints)
391
        {
392
        case 0:
393
        case 1: 
394
        case 2: break;
395
        case 3: vc.removeAllElements();
396
                break;
397
        default:vc.removeElementAt(location);
398
        }
399

    
400
      numPoints--;
401
      cacheDirty = true; 
402
      return true;
403
      }
404

    
405
   return false;
406
   }
407
  
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409
/**
410
 * Removes all Points.
411
 */
412
  public synchronized void removeAll()
413
    {
414
    numPoints = 0;
415
    vv.removeAllElements();
416
    vc.removeAllElements();
417
    cacheDirty = false;
418
   
419
    if( vn!=null ) vn.removeAllElements();
420
    }
421

    
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423
/**
424
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
425
 * <p>
426
 * Since this is a 2-dimensional Interpolator, the resulting interpolated Float2D gets written
427
 * to two locations in the buffer: buffer[offset] and buffer[offset+1]. 
428
 * 
429
 * @param buffer Float buffer we will write the resulting Float2D to.
430
 * @param offset Offset in the buffer where to write the result.
431
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
432
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
433
 */  
434
  public synchronized void interpolate(float[] buffer, int offset, float time)
435
    {
436
    switch(numPoints)
437
      {
438
      case 0: buffer[offset  ] = 0.0f;
439
              buffer[offset+1] = 0.0f;
440
              break;
441
      case 1: curr = vv.elementAt(0);
442
              buffer[offset  ] = curr.x;
443
              buffer[offset+1] = curr.y;
444
              break;
445
      case 2: curr = vv.elementAt(0);
446
              next = vv.elementAt(1);
447
               
448
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
449
             
450
              if( vn!=null )
451
                {
452
                time = noise(time,0);
453
              
454
                float dx2 = next.x-curr.x;
455
                float dy2 = next.y-curr.y;
456
   
457
                buffer[offset  ] = dx2*time + curr.x +dy2*mFactor;
458
                buffer[offset+1] = dy2*time + curr.y -dx2*mFactor;
459
                }
460
              else
461
                {
462
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
463
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
464
                }
465
              
466
              break;
467
      default:float t = time;
468
        
469
              switch(mMode)
470
                {
471
                case MODE_LOOP: time = time*numPoints;
472
                                break;
473
                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
474
                                break;
475
                case MODE_JUMP: time = time*(numPoints-1);
476
                                break;
477
                }
478
            
479
              int vecCurr = (int)time;
480
              time = time-vecCurr;
481
      
482
              if( vecCurr>=0 && vecCurr<numPoints )
483
                { 
484
                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
485
                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
486
                  {
487
                  int vecNext;   
488
                  mVecCurr = vecCurr;
489
                                
490
                  switch(mMode)
491
                    {
492
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
493
                                    break;
494
                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
495
                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
496
                                    break;
497
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
498
                                    break;
499
                    default       : vecNext = 0;                
500
                    }
501
              
502
                  next = vv.elementAt(vecNext);
503
                  tmp2 = vc.elementAt(vecNext);
504
              
505
                  if( tmp2.vx!=next.x || tmp2.vy!=next.y ) recomputeCache();
506
                  }
507
              
508
                if( vn!=null )
509
                  {
510
                  time = noise(time,vecCurr);
511
                  tmp1 = vc.elementAt(vecCurr);
512
               
513
                  float dx2 = (3*tmp1.ax*time+2*tmp1.bx)*time + tmp1.cx;
514
                  float dy2 = (3*tmp1.ay*time+2*tmp1.by)*time + tmp1.cy;
515
                 
516
                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx +dy2*mFactor;
517
                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy -dx2*mFactor;
518
                  } 
519
                else
520
                  {
521
                  tmp1 = vc.elementAt(vecCurr);
522
                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
523
                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
524
                  }
525
                
526
                break;
527
                }
528
      }
529
    }  
530
  }
531
///////////////////////////////////////////////////////////////////////////////////////////////////
532
//
(27-27/30)