Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic1D.java @ c45c2ab1

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 Static1Ds.
28
*/
29

    
30
public class Dynamic1D extends Dynamic implements Data1D
31
  {
32
  private Vector<Static1D> vv;
33
  private Static1D prev, curr, next;
34

    
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36
// no array bounds checking!
37
  
38
  private void vec(int c)
39
    {
40
    int p = c>0 ? c-1: numPoints-1;
41
    int n = c<numPoints-1 ? c+1: 0;
42
    
43
    prev = vv.elementAt(p);
44
    curr = vv.elementAt(c);
45
    next = vv.elementAt(n);
46

    
47
    tmp1 = vc.elementAt(c);
48
    
49
    float px = curr.x - prev.x;
50
    float nx = next.x - curr.x;
51
     
52
    float d = nx*nx;
53
    
54
    if( d>0 )
55
      {
56
      float q = (float)Math.sqrt((px*px)/d);
57
      
58
      if( q>1 )
59
        {
60
        tmp1.tangent[0] = nx+px/q;
61
        }
62
      else
63
        {
64
        tmp1.tangent[0] = px+nx*q;
65
        }
66
      }
67
    else
68
      {
69
      tmp1.tangent[0] = 0.0f;
70
      }
71
    }
72
      
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74
  
75
  private void recomputeCache()
76
    {  
77
    if( numPoints==1 )
78
      {
79
      tmp1= vc.elementAt(0);
80
      curr= vv.elementAt(0);
81
        
82
      tmp1.a[0] = 0.0f;
83
      tmp1.b[0] = 0.0f;
84
      tmp1.c[0] = curr.x;
85
      tmp1.d[0] = 0.0f;
86
      }
87
    else if( numPoints==2 )
88
      {
89
      tmp1= vc.elementAt(0);
90
      tmp2= vc.elementAt(1);
91
      curr= vv.elementAt(0);
92
      next= vv.elementAt(1);
93
          
94
      tmp1.a[0] = 0.0f;
95
      tmp1.b[0] = 0.0f;
96
      tmp1.c[0] = next.x - curr.x;
97
      tmp1.d[0] = curr.x;
98
      
99
      tmp2.a[0] = 0.0f;
100
      tmp2.b[0] = 0.0f;
101
      tmp2.c[0] = curr.x - next.x;
102
      tmp2.d[0] = next.x;
103
      }
104
    else
105
      {
106
      int i, n;  
107
         
108
      for(i=0; i<numPoints; i++) vec(i);
109
   
110
      for(i=0; i<numPoints; i++)
111
        {
112
        n = i<numPoints-1 ? i+1:0;  
113
      
114
        tmp1= vc.elementAt(i);
115
        tmp2= vc.elementAt(n);
116
        curr= vv.elementAt(i);
117
        next= vv.elementAt(n);
118
    
119
        tmp1.cached[0] = curr.x;
120
        
121
        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
122
        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
123
        tmp1.c[0] = tmp1.tangent[0];
124
        tmp1.d[0] = curr.x;
125
        }
126
      }
127
   
128
    cacheDirty = false;
129
    }
130

    
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132
// PUBLIC API
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134
/**
135
 * Default constructor.
136
 */
137
  public Dynamic1D()
138
    {
139
    super(0,0.5f,1);
140
    vv = new Vector<>();
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
/**
146
 * Constructor setting the speed of interpolation and the number of revolutions.
147
 *
148
 * What constitutes 'one revolution' depends on the MODE:
149
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
150
 *
151
 * @param duration number of milliseconds it takes to do one revolution.
152
 * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
153
 */
154
  public Dynamic1D(int duration, float count)
155
    {
156
    super(duration,count,1);
157
    vv = new Vector<>();
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161
/**
162
 * Returns the location'th Static1D.
163
 *   
164
 * @param location the index of the Point we are interested in.
165
 * @return The Static1D, if 0<=location&lt;getNumPoints(), or null otherwise.
166
 */
167
  public synchronized Static1D getPoint(int location)
168
    {
169
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
170
    }
171
  
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173
/**
174
 * Resets the location'th Point.
175
 * 
176
 * @param location the index of the Point we are setting.
177
 * @param x New value of its first float.
178
 */
179
  public synchronized void setPoint(int location, float x)
180
    {
181
    if( location>=0 && location<numPoints )
182
      {
183
      curr = vv.elementAt(location);
184
   
185
      if( curr!=null )
186
        {
187
        curr.set(x);
188
        cacheDirty=true;
189
        }
190
      }
191
    }
192
 
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194
/**
195
 * Adds a new Static1D to the end of our list of Points to interpolate through.
196
 * <p>   
197
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
198
 * here, and later on {@link Static1D#set(float)} it to some new value and the change will
199
 * be seamlessly reflected in the interpolated path.  
200
 * <p>
201
 * A Point can be added multiple times.
202
 *   
203
 * @param v The Point to add.
204
 */
205
  public synchronized void add(Static1D v)
206
    {
207
    if( v!=null )
208
      {
209
      vv.add(v);
210
     
211
      if( vn!=null ) vn.add(new VectorNoise());
212
       
213
      switch(numPoints)
214
        {
215
        case 0: 
216
        case 1: break;
217
        case 2: vc.add(new VectorCache());
218
                vc.add(new VectorCache());
219
                vc.add(new VectorCache());
220
                cacheDirty = true;
221
                break;
222
        default:vc.add(new VectorCache());
223
                cacheDirty = true;
224
        }
225
     
226
      numPoints++;
227
      }
228
    }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231
/**
232
 * Adds a new Static1D to the location'th place in our List of Points to interpolate through.
233
 *   
234
 * @param location Index in our List to add the new Point at.
235
 * @param v The Point to add.
236
 */
237
  public synchronized void add(int location, Static1D v)
238
    {
239
    if( v!=null )
240
      {
241
      vv.add(location, v);
242
      
243
      if( vn!=null ) vn.add(new VectorNoise());
244
             
245
      switch(numPoints)
246
        {
247
        case 0:
248
        case 1: break;
249
        case 2: vc.add(new VectorCache());
250
                vc.add(new VectorCache());
251
                vc.add(new VectorCache());
252
                cacheDirty = true;
253
                break;
254
        default:vc.add(location,new VectorCache());
255
                cacheDirty = true;
256
        }
257
      
258
      numPoints++;
259
      }
260
    }
261
  
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263
/**
264
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
265
 * 
266
 * @param v The Point to remove.
267
 * @return <code>true</code> if we have removed at least one Point.
268
 */
269
  public synchronized boolean remove(Static1D v)
270
    {
271
    int n = vv.indexOf(v);
272
    boolean found = false;
273
   
274
    while( n>=0 ) 
275
      {
276
      vv.remove(n);
277
     
278
      if( vn!=null ) vn.remove(0);
279
     
280
      switch(numPoints)
281
        {
282
        case 0:
283
        case 1:
284
        case 2: break;
285
        case 3: vc.removeAllElements();
286
                break;
287
        default:vc.remove(n);
288
                cacheDirty=true;
289
        }
290

    
291
      numPoints--;
292
      found = true;
293
      n = vv.indexOf(v);
294
      }
295
   
296
    return found;
297
    }
298

    
299
///////////////////////////////////////////////////////////////////////////////////////////////////
300
/**
301
 * Removes a location'th Point from the List of Points we interpolate through.
302
 * 
303
 * @param location index of the Point we want to remove. 
304
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
305
 */
306
  public synchronized boolean remove(int location)
307
    {
308
    if( location>=0 && location<numPoints ) 
309
      {
310
      vv.removeElementAt(location);
311
      
312
      if( vn!=null ) vn.remove(0);
313
     
314
      switch(numPoints)
315
        {
316
        case 0:
317
        case 1: 
318
        case 2: break;
319
        case 3: vc.removeAllElements();
320
                break;
321
        default:vc.removeElementAt(location);
322
        }
323

    
324
      numPoints--;
325
      cacheDirty = true; 
326
      return true;
327
      }
328

    
329
   return false;
330
   }
331
  
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333
/**
334
 * Removes all Points.
335
 */
336
  public synchronized void removeAll()
337
    {
338
    numPoints = 0;
339
    vv.removeAllElements();
340
    vc.removeAllElements();
341
    cacheDirty = false;
342
   
343
    if( vn!=null ) vn.removeAllElements();
344
    }
345

    
346
///////////////////////////////////////////////////////////////////////////////////////////////////
347
/**
348
 * Sets the 'smoothness' of interpolation.
349
 * <p>
350
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
351
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
352
 * up and slowing down, etc.
353
 *
354
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
355
 */
356

    
357
  public synchronized void setNoise(Static1D noise)
358
    {
359
    if( vn==null )
360
      {
361
      vn = new Vector<>();
362
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
363

    
364
      if( mDimension>=2 )
365
        {
366
        mFactor = new float[mDimension-1];
367
        }
368

    
369
      mNoise = new float[mDimension];
370
      }
371

    
372
    if( noise.x<0.0f ) noise.x = 0.0f;
373
    if( noise.x>1.0f ) noise.x = 1.0f;
374

    
375
    mNoise[0] = noise.x;
376
    }
377

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379

    
380
  synchronized void interpolate(float[] buffer, int offset, float time)
381
    {
382
    switch(numPoints)
383
      {
384
      case 0: buffer[offset] = 0.0f;
385
              break;
386
      case 1: curr = vv.elementAt(0);
387
              buffer[offset] = curr.x;
388
              break;
389
      case 2: curr = vv.elementAt(0);
390
              next = vv.elementAt(1);
391

    
392
              int segment2= (int)(2*time);
393

    
394
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
395

    
396
              if( vn!=null )
397
                {
398
                if( segment2 != mSegment )
399
                  {
400
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
401
                  mSegment = segment2;
402
                  }
403

    
404
                time = noise(time,0);
405
                }
406
             
407
              buffer[offset] = (next.x-curr.x)*time + curr.x;
408
              break;
409
      default:float t = time;
410
              int vecCurr, segment;
411

    
412
              switch(mMode)
413
                {
414
                case MODE_LOOP: time = time*numPoints;
415
                                segment = (int)time;
416
                                vecCurr = segment;
417
                                break;
418
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
419

    
420
                                if( t<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
421
                                  {            // and end position is slightly not equal to the end point => might not get autodeleted!
422
                                  time = 2*t*(numPoints-1);
423
                                  vecCurr = segment;
424
                                  }
425
                                else
426
                                  {
427
                                  time = 2*(1-t)*(numPoints-1);
428
                                  vecCurr = 2*numPoints-3-segment;
429
                                  }
430
                                break;
431
                case MODE_JUMP: time = time*(numPoints-1);
432
                                segment = (int)time;
433
                                vecCurr = segment;
434
                                break;
435
                default       : vecCurr = 0;
436
                                segment = 0;
437
                }
438

    
439
              if( vecCurr>=0 && vecCurr<numPoints )
440
                {
441
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
442
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
443
                  {
444
                  int vecNext;
445

    
446
                  switch(mMode)
447
                    {
448
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
449
                                    break;
450
                    case MODE_PATH: if( t<=0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
451
                                    else          vecNext = vecCurr==0 ? 1 : vecCurr-1;
452
                                    break;
453
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
454
                                    break;
455
                    default       : vecNext = 0;
456
                    }
457
              
458
                  next = vv.elementAt(vecNext);
459
                  tmp2 = vc.elementAt(vecNext);
460
              
461
                  if( tmp2.cached[0]!=next.x ) recomputeCache();
462
                  }
463

    
464
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
465

    
466
                mSegment = segment;
467

    
468
                time = time-vecCurr;
469

    
470
                tmp1 = vc.elementAt(vecCurr);
471

    
472
                if( vn!=null )
473
                  {
474
                  time = noise(time,vecCurr);
475
                  }
476
            
477
                buffer[offset] = ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
478
                break;
479
                }
480
        }
481
     }  
482
  
483
  }
484
///////////////////////////////////////////////////////////////////////////////////////////////////
485
//
(7-7/18)