Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic2D.java @ 8c57d77b

1
////////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// This library is free software; you can redistribute it and/or                                 //
7
// modify it under the terms of the GNU Lesser General Public                                    //
8
// License as published by the Free Software Foundation; either                                  //
9
// version 2.1 of the License, or (at your option) any later version.                            //
10
//                                                                                               //
11
// This library 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 GNU                             //
14
// Lesser General Public License for more details.                                               //
15
//                                                                                               //
16
// You should have received a copy of the GNU Lesser General Public                              //
17
// License along with this library; if not, write to the Free Software                           //
18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
package org.distorted.library.type;
22

    
23
import java.util.Vector;
24

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26
/** 
27
* A 2-dimensional implementation of the Dynamic class to interpolate between a list
28
* of Static2Ds.
29
*/
30

    
31
public class Dynamic2D extends Dynamic implements Data2D
32
  {
33
  private Vector<Static2D> vv;
34
  private Static2D prev, curr, next;
35

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

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

    
133
        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
134
        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
135
        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
136
        tmpCache1.d[0] = curr.x;
137

    
138
        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
139
        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
140
        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
141
        tmpCache1.d[1] = curr.y;
142

    
143
        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
144
        }
145
      }
146
    
147
    cacheDirty = false;
148
    }
149

    
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151
// PUBLIC API 
152
///////////////////////////////////////////////////////////////////////////////////////////////////
153
/**
154
 * Default constructor.
155
 */
156
  public Dynamic2D()
157
    {
158
    super(0,0.5f,2);
159
    vv = new Vector<>();
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163
/**
164
 * Constructor setting the speed of interpolation and the number of revolutions.
165
 *
166
 * What constitutes 'one revolution' depends on the MODE:
167
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
168
 *
169
 * @param duration number of milliseconds it takes to do one revolution.
170
 * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
171
 */
172
  public Dynamic2D(int duration, float count)
173
    {
174
    super(duration,count,2);
175
    vv = new Vector<>();
176
    }
177

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

    
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248
/**
249
 * Adds a new Static2D to the location'th place in our List of Points to interpolate through.
250
 *   
251
 * @param location Index in our List to add the new Point at.
252
 * @param v The Point to add.
253
 */  
254
  public synchronized void add(int location, Static2D v)
255
    {
256
    if( v!=null )
257
      {
258
      vv.add(location, v);
259
      
260
      if( vn!=null ) vn.add(new VectorNoise());
261
      
262
      switch(numPoints)
263
        {
264
        case 0:
265
        case 1: break;
266
        case 2: vc.add(new VectorCache());
267
                vc.add(new VectorCache());
268
                vc.add(new VectorCache());
269
                break;
270
        default:vc.add(location,new VectorCache());
271
        }
272
      
273
      numPoints++;
274
      cacheDirty = true;
275
      }
276
    }
277
  
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279
/**
280
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
281
 * 
282
 * @param v The Point to remove.
283
 * @return <code>true</code> if we have removed at least one Point.
284
 */
285
  public synchronized boolean remove(Static2D v)
286
    {
287
    int n = vv.indexOf(v);
288
    boolean found = false;
289
   
290
    while( n>=0 ) 
291
      {
292
      vv.remove(n);
293
     
294
      if( vn!=null ) vn.remove(0);
295
     
296
      switch(numPoints)
297
        {
298
        case 0:
299
        case 1: 
300
        case 2: break;
301
        case 3: vc.removeAllElements();
302
                break;
303
        default:vc.remove(n);
304
        }
305
     
306
      numPoints--;
307
      found = true;
308
      n = vv.indexOf(v);
309
      }
310
   
311
    if( found ) 
312
      {
313
      cacheDirty=true;
314
      }
315
   
316
    return found;
317
    }
318

    
319
///////////////////////////////////////////////////////////////////////////////////////////////////
320
/**
321
 * Removes a location'th Point from the List of Points we interpolate through.
322
 * 
323
 * @param location index of the Point we want to remove. 
324
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
325
 */
326
  public synchronized boolean remove(int location)
327
    {
328
    if( location>=0 && location<numPoints ) 
329
      {
330
      vv.removeElementAt(location);
331
      
332
      if( vn!=null ) vn.remove(0);
333
      
334
      switch(numPoints)
335
        {
336
        case 0:
337
        case 1: 
338
        case 2: break;
339
        case 3: vc.removeAllElements();
340
                break;
341
        default:vc.removeElementAt(location);
342
        }
343

    
344
      numPoints--;
345
      cacheDirty = true; 
346
      return true;
347
      }
348

    
349
   return false;
350
   }
351
  
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353
/**
354
 * Removes all Points.
355
 */
356
  public synchronized void removeAll()
357
    {
358
    numPoints = 0;
359
    vv.removeAllElements();
360
    vc.removeAllElements();
361
    cacheDirty = false;
362
   
363
    if( vn!=null ) vn.removeAllElements();
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367
/**
368
 * Sets the 'smoothness' of interpolation.
369
 * <p>
370
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
371
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
372
 * up and slowing down, etc.
373
 *
374
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
375
 */
376

    
377
  public synchronized void setNoise(Static2D noise)
378
    {
379
    if( vn==null )
380
      {
381
      vn = new Vector<>();
382
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
383

    
384
      if( mDimension>=2 )
385
        {
386
        mFactor = new float[mDimension-1];
387
        }
388

    
389
      mNoise = new float[mDimension];
390
      }
391

    
392
    if( noise.x<0.0f ) noise.x = 0.0f;
393
    if( noise.x>1.0f ) noise.x = 1.0f;
394
    if( noise.y<0.0f ) noise.y = 0.0f;
395
    if( noise.y>1.0f ) noise.y = 1.0f;
396

    
397
    mNoise[0] = noise.x;
398
    mNoise[1] = noise.y;
399
    }
400

    
401
///////////////////////////////////////////////////////////////////////////////////////////////////
402

    
403
  synchronized void interpolate(float[] buffer, int offset, float time)
404
    {
405
    switch(numPoints)
406
      {
407
      case 0: buffer[offset  ] = 0.0f;
408
              buffer[offset+1] = 0.0f;
409
              break;
410
      case 1: curr = vv.elementAt(0);
411
              buffer[offset  ] = curr.x;
412
              buffer[offset+1] = curr.y;
413
              break;
414
      case 2: curr = vv.elementAt(0);
415
              next = vv.elementAt(1);
416

    
417
              int segment= (int)(2*time);
418

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

    
421
              if( vn!=null )
422
                {
423
                if( segment != mSegment )
424
                  {
425
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
426
                  mSegment = segment;
427
                  }
428

    
429
                time = noise(time,0);
430
              
431
                baseV[1][0] = next.x-curr.x;
432
                baseV[1][1] = next.y-curr.y;
433
   
434
                buffer[offset  ] = baseV[1][0]*time + curr.x +baseV[1][1]*mFactor[0];
435
                buffer[offset+1] = baseV[1][1]*time + curr.y -baseV[1][0]*mFactor[0];
436
                }
437
              else
438
                {
439
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
440
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
441
                }
442
              
443
              break;
444
      default:computeSegmentAndTime(time);
445

    
446
              if( mTmpVec>=0 && mTmpVec<numPoints )
447
                {
448
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
449
                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
450
                  {
451
                  int vecNext = getNext(mTmpVec,time);
452
                  next = vv.elementAt(vecNext);
453
                  tmpCache2 = vc.elementAt(vecNext);
454

    
455
                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y ) recomputeCache();
456
                  }
457

    
458
                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
459

    
460
                mSegment = mTmpSeg;
461
                time = mTmpTime-mTmpVec;
462
                tmpCache1 = vc.elementAt(mTmpVec);
463
                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
464

    
465
                if( vn!=null )
466
                  {
467
                  time = noise(time,mTmpVec);
468

    
469
                  baseV[1][0] = (3*tmpCache1.a[0]*time + 2*tmpCache1.b[0])*time + tmpCache1.c[0];
470
                  baseV[1][1] = (3*tmpCache1.a[1]*time + 2*tmpCache1.b[1])*time + tmpCache1.c[1];
471
                 
472
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] +baseV[1][1]*mFactor[0];
473
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] -baseV[1][0]*mFactor[0];
474
                  } 
475
                else
476
                  {
477
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
478
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
479
                  }
480
                
481
                break;
482
                }
483
      }
484
    }  
485
  }
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487
//
(8-8/18)