Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic1D.java @ 8c893ffc

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted is distributed in the hope that it will be useful,                                  //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.library.type;
21

    
22
import java.util.Vector;
23

    
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
/** 
26
* A 1-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Float1Ds.
28
*/
29

    
30
public class Dynamic1D extends Dynamic implements Data1D
31
  {
32
  
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
// the coefficients of the X(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
35
// (x) is the vector tangent to the path.
36
// (vx) is the original vector from vv (copied here so when interpolating we can see if it is 
37
// still valid and if not - rebuild the Cache
38
   
39
  private class VectorCache
40
    {
41
    float ax, bx, cx, dx;
42
   
43
    float x;
44
    float vx;
45
    }
46
  
47
  private class VectorNoise
48
    {
49
    float[] nx;
50
   
51
    public VectorNoise()
52
      {
53
      nx = new float[NUM_NOISE]; 
54
      nx[0] = mRnd.nextFloat();
55
      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
56
      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
57
      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
58
      }
59
    }
60
  
61
  private Vector<VectorCache> vc;
62
  private VectorCache tmp1, tmp2;
63
 
64
  private Vector<Static1D> vv;
65
  private Static1D prev, curr, next;
66
 
67
  private Vector<VectorNoise> vn;
68
  private VectorNoise tmpN;
69
  
70
///////////////////////////////////////////////////////////////////////////////////////////////////
71

    
72
  synchronized void createNoise()
73
    {
74
    if( vn==null )
75
      {
76
      vn = new Vector<VectorNoise>();
77
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
78
      }
79
    }
80
  
81
///////////////////////////////////////////////////////////////////////////////////////////////////
82
// no array bounds checking!
83
  
84
  private void vec(int c)
85
    {
86
    int p = c>0 ? c-1: numPoints-1;
87
    int n = c<numPoints-1 ? c+1: 0;
88
    
89
    prev = vv.elementAt(p);
90
    curr = vv.elementAt(c);
91
    next = vv.elementAt(n);
92

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

    
179
  private float noise(float time,int vecNum)
180
    {
181
    float lower, upper, len;  
182
    float d = time*(NUM_NOISE+1);
183
    int index = (int)d;
184
    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
185
    tmpN = vn.elementAt(vecNum);
186
   
187
    if( index==0 )
188
      {
189
      len = 1.0f/(NUM_NOISE+1);  
190
      return (len + mNoise*(tmpN.nx[0]-len))*d;
191
      }
192
    if( index==NUM_NOISE )
193
      {
194
      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
195
      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);   
196
      return (1.0f-lower)*(d-NUM_NOISE) + lower;   
197
      }
198
   
199
    len = ((float)index)/(NUM_NOISE+1);
200
    lower = len + mNoise*(tmpN.nx[index-1]-len);   
201
    len = ((float)index+1)/(NUM_NOISE+1); 
202
    upper = len + mNoise*(tmpN.nx[index  ]-len);
203
            
204
    return (upper-lower)*(d-index) + lower; 
205
    }
206
   
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208
// PUBLIC API
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
/**
211
 * Default constructor.
212
 */
213
  public Dynamic1D()
214
    {
215
    vv = new Vector<>();
216
    vc = new Vector<>();
217
    vn = null;
218
    numPoints = 0;
219
    cacheDirty = false;
220
    mMode = MODE_LOOP;
221
    mDuration = 0;
222
    mCount = 0.5f;
223
    }
224

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226

    
227
/**
228
 * Default constructor.
229
 *
230
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
231
 *                 last and back to the first
232
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
233
 *                 to the last, back to first, and to the last again.
234
 */
235
  public Dynamic1D(int duration, float count)
236
    {
237
    vv = new Vector<>();
238
    vc = new Vector<>();
239
    vn = null;
240
    numPoints = 0;
241
    cacheDirty = false;
242
    mMode = MODE_LOOP;
243
    mDuration = duration;
244
    mCount = count;
245
    }
246

    
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248
/**
249
 * Returns the location'th Static1D.
250
 *   
251
 * @param location the index of the Point we are interested in.
252
 * @return The Static1D, if 0<=location&lt;getNumPoints(), or null otherwise.
253
 */
254
  public synchronized Static1D getPoint(int location)
255
    {
256
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
257
    }
258
  
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260
/**
261
 * Resets the location'th Point.
262
 * 
263
 * @param location the index of the Point we are setting.
264
 * @param x New value of its first float.
265
 */
266
  public synchronized void setPoint(int location, float x)
267
    {
268
    if( location>=0 && location<numPoints )
269
      {
270
      curr = vv.elementAt(location);
271
   
272
      if( curr!=null )
273
        {
274
        curr.set(x);
275
        cacheDirty=true;
276
        }
277
      }
278
    }
279
 
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
/**
282
 * Adds a new Static1D to the end of our list of Points to interpolate through.
283
 * <p>   
284
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
285
 * here, and later on {@link Static1D#set(float)} it to some new value and the change will
286
 * be seamlessly reflected in the interpolated path.  
287
 * <p>
288
 * A Point can be added multiple times.
289
 *   
290
 * @param v The Point to add.
291
 */
292
  public synchronized void add(Static1D v)
293
    {
294
    if( v!=null )
295
      {
296
      vv.add(v);
297
     
298
      if( vn!=null ) vn.add(new VectorNoise());
299
       
300
      switch(numPoints)
301
        {
302
        case 0: 
303
        case 1: break;
304
        case 2: vc.add(new VectorCache());
305
                vc.add(new VectorCache());
306
                vc.add(new VectorCache());
307
                cacheDirty = true;
308
                break;
309
        default:vc.add(new VectorCache());
310
                cacheDirty = true;
311
        }
312
     
313
      numPoints++;
314
      }
315
    }
316

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

    
378
      numPoints--;
379
      found = true;
380
      n = vv.indexOf(v);
381
      }
382
   
383
    return found;
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387
/**
388
 * Removes a location'th Point from the List of Points we interpolate through.
389
 * 
390
 * @param location index of the Point we want to remove. 
391
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
392
 */
393
  public synchronized boolean remove(int location)
394
    {
395
    if( location>=0 && location<numPoints ) 
396
      {
397
      vv.removeElementAt(location);
398
      
399
      if( vn!=null ) vn.remove(0);
400
     
401
      switch(numPoints)
402
        {
403
        case 0:
404
        case 1: 
405
        case 2: break;
406
        case 3: vc.removeAllElements();
407
                break;
408
        default:vc.removeElementAt(location);
409
        }
410

    
411
      numPoints--;
412
      cacheDirty = true; 
413
      return true;
414
      }
415

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