Project

General

Profile

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

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

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 computeVelocity(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
    tmpCache1 = 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
        tmpCache1.velocity[0] = nx+px/q;
61
        }
62
      else
63
        {
64
        tmpCache1.velocity[0] = px+nx*q;
65
        }
66
      }
67
    else
68
      {
69
      tmpCache1.velocity[0] = 0.0f;
70
      }
71
    }
72
      
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74
  
75
  private void recomputeCache()
76
    {  
77
    if( numPoints==1 )
78
      {
79
      tmpCache1 = vc.elementAt(0);
80
      curr= vv.elementAt(0);
81
        
82
      tmpCache1.a[0] = 0.0f;
83
      tmpCache1.b[0] = 0.0f;
84
      tmpCache1.c[0] = curr.x;
85
      tmpCache1.d[0] = 0.0f;
86
      }
87
    else if( numPoints==2 )
88
      {
89
      tmpCache1 = vc.elementAt(0);
90
      tmpCache2 = vc.elementAt(1);
91
      curr= vv.elementAt(0);
92
      next= vv.elementAt(1);
93
          
94
      tmpCache1.a[0] = 0.0f;
95
      tmpCache1.b[0] = 0.0f;
96
      tmpCache1.c[0] = next.x - curr.x;
97
      tmpCache1.d[0] = curr.x;
98
      
99
      tmpCache2.a[0] = 0.0f;
100
      tmpCache2.b[0] = 0.0f;
101
      tmpCache2.c[0] = curr.x - next.x;
102
      tmpCache2.d[0] = next.x;
103
      }
104
    else
105
      {
106
      int i, n;  
107
         
108
      for(i=0; i<numPoints; i++) computeVelocity(i);
109
   
110
      for(i=0; i<numPoints; i++)
111
        {
112
        n = i<numPoints-1 ? i+1:0;  
113
      
114
        tmpCache1 = vc.elementAt(i);
115
        tmpCache2 = vc.elementAt(n);
116
        curr= vv.elementAt(i);
117
        next= vv.elementAt(n);
118
    
119
        tmpCache1.cached[0] = curr.x;
120
        
121
        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
122
        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
123
        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
124
        tmpCache1.d[0] = curr.x;
125

    
126
        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
127
        }
128
      }
129
   
130
    cacheDirty = false;
131
    }
132

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
379
///////////////////////////////////////////////////////////////////////////////////////////////////
380

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

    
393
              int segment= (int)(2*time);
394

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

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

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

    
412
              if( mTmpVec>=0 && mTmpVec<numPoints )
413
                {
414
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
415
                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
416
                  {
417
                  int vecNext = getNext(mTmpVec,time);
418
                  next = vv.elementAt(vecNext);
419
                  tmpCache2 = vc.elementAt(vecNext);
420

    
421
                  if( tmpCache2.cached[0]!=next.x ) recomputeCache();
422
                  }
423

    
424
                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
425

    
426
                mSegment = mTmpSeg;
427
                time = mTmpTime-mTmpVec;
428
                tmpCache1 = vc.elementAt(mTmpVec);
429
                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
430

    
431
                if( vn!=null )
432
                  {
433
                  time = noise(time,mTmpVec);
434
                  }
435
            
436
                buffer[offset] = ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
437
                break;
438
                }
439
        }
440
     }  
441
  
442
  }
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444
//
(7-7/18)