Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic2D.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 2-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static2Ds.
28
*/
29

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401

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

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

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

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

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

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

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

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

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

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

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