Project

General

Profile

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

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

1 2c8310b1 Leszek Koltunski
////////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 d333eb6b Leszek Koltunski
//                                                                                               //
4 46b572b5 Leszek Koltunski
// This file is part of Distorted.                                                               //
5 d333eb6b Leszek Koltunski
//                                                                                               //
6 2c8310b1 Leszek Koltunski
// 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 d333eb6b Leszek Koltunski
//                                                                                               //
11 2c8310b1 Leszek Koltunski
// This library is distributed in the hope that it will be useful,                               //
12 d333eb6b Leszek Koltunski
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13 2c8310b1 Leszek Koltunski
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                             //
14
// Lesser General Public License for more details.                                               //
15 d333eb6b Leszek Koltunski
//                                                                                               //
16 2c8310b1 Leszek Koltunski
// 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 d333eb6b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
20
21 a4835695 Leszek Koltunski
package org.distorted.library.type;
22 6a06a912 Leszek Koltunski
23
import java.util.Vector;
24
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26
/** 
27 568b29d8 Leszek Koltunski
* A 2-dimensional implementation of the Dynamic class to interpolate between a list
28 350cc2f5 Leszek Koltunski
* of Static2Ds.
29 6a06a912 Leszek Koltunski
*/
30
31 568b29d8 Leszek Koltunski
public class Dynamic2D extends Dynamic implements Data2D
32 6a06a912 Leszek Koltunski
  {
33 568b29d8 Leszek Koltunski
  private Vector<Static2D> vv;
34
  private Static2D prev, curr, next;
35 6a06a912 Leszek Koltunski
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37
// no array bounds checking!
38
  
39 f871c455 Leszek Koltunski
  private void computeVelocity(int c)
40 6a06a912 Leszek Koltunski
    {
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 9aabc9eb Leszek Koltunski
    tmpCache1 = vc.elementAt(c);
49 6a06a912 Leszek Koltunski
    
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 9aabc9eb Leszek Koltunski
        tmpCache1.velocity[0] = nx+px/q;
64
        tmpCache1.velocity[1] = ny+py/q;
65 6a06a912 Leszek Koltunski
        }
66
      else
67
        {
68 9aabc9eb Leszek Koltunski
        tmpCache1.velocity[0] = px+nx*q;
69
        tmpCache1.velocity[1] = py+ny*q;
70 6a06a912 Leszek Koltunski
        }
71
      }
72
    else
73
      {
74 9aabc9eb Leszek Koltunski
      tmpCache1.velocity[0] = 0.0f;
75
      tmpCache1.velocity[1] = 0.0f;
76 6a06a912 Leszek Koltunski
      }
77
    }
78
   
79
///////////////////////////////////////////////////////////////////////////////////////////////////
80
  
81
  private void recomputeCache()
82
    {  
83
    if( numPoints==1 )
84
      {
85 9aabc9eb Leszek Koltunski
      tmpCache1 = vc.elementAt(0);
86 6a06a912 Leszek Koltunski
      curr= vv.elementAt(0);
87
              
88 9aabc9eb Leszek Koltunski
      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 6a06a912 Leszek Koltunski
      }
94
    else if( numPoints==2 )
95
      {
96 9aabc9eb Leszek Koltunski
      tmpCache1 = vc.elementAt(0);
97
      tmpCache2 = vc.elementAt(1);
98 6a06a912 Leszek Koltunski
      curr= vv.elementAt(0);
99
      next= vv.elementAt(1);
100
          
101 9aabc9eb Leszek Koltunski
      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 6a06a912 Leszek Koltunski
      
108 9aabc9eb Leszek Koltunski
      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 6a06a912 Leszek Koltunski
      }
115
    else
116
      {
117
      int i, n;  
118
         
119 f871c455 Leszek Koltunski
      for(i=0; i<numPoints; i++) computeVelocity(i);
120 6a06a912 Leszek Koltunski
   
121
      for(i=0; i<numPoints; i++)
122
        {
123
        n = i<numPoints-1 ? i+1:0;  
124
      
125 9aabc9eb Leszek Koltunski
        tmpCache1 = vc.elementAt(i);
126
        tmpCache2 = vc.elementAt(n);
127 6a06a912 Leszek Koltunski
        curr= vv.elementAt(i);
128
        next= vv.elementAt(n);
129
      
130 9aabc9eb Leszek Koltunski
        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 6a06a912 Leszek Koltunski
        }
145
      }
146
    
147
    cacheDirty = false;
148
    }
149
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151
// PUBLIC API 
152
///////////////////////////////////////////////////////////////////////////////////////////////////
153
/**
154
 * Default constructor.
155
 */
156 568b29d8 Leszek Koltunski
  public Dynamic2D()
157 6a06a912 Leszek Koltunski
    {
158 649544b8 Leszek Koltunski
    super(0,0.5f,2);
159
    vv = new Vector<>();
160 6a06a912 Leszek Koltunski
    }
161 8c893ffc Leszek Koltunski
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163
/**
164 c45c2ab1 Leszek Koltunski
 * 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 8c893ffc Leszek Koltunski
 *
169 c45c2ab1 Leszek Koltunski
 * @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 8c893ffc Leszek Koltunski
 */
172
  public Dynamic2D(int duration, float count)
173
    {
174 649544b8 Leszek Koltunski
    super(duration,count,2);
175 8c893ffc Leszek Koltunski
    vv = new Vector<>();
176
    }
177
178 6a06a912 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
179
/**
180 568b29d8 Leszek Koltunski
 * Returns the location'th Static2D.
181 6a06a912 Leszek Koltunski
 *   
182
 * @param location the index of the Point we are interested in.
183 568b29d8 Leszek Koltunski
 * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
184 6a06a912 Leszek Koltunski
 */  
185 568b29d8 Leszek Koltunski
  public synchronized Static2D getPoint(int location)
186 6a06a912 Leszek Koltunski
    {
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 568b29d8 Leszek Koltunski
 * Adds a new Static2D to the end of our list of Points to interpolate through.
214 6a06a912 Leszek Koltunski
 * <p>   
215
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
216 568b29d8 Leszek Koltunski
 * here, and later on {@link Static2D#set(float,float)} it to some new value and the change
217 6a06a912 Leszek Koltunski
 * 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 568b29d8 Leszek Koltunski
  public synchronized void add(Static2D v)
224 6a06a912 Leszek Koltunski
    {
225
    if( v!=null )
226
      {
227
      vv.add(v);
228
     
229 291705f6 Leszek Koltunski
      if( vn!=null ) vn.add(new VectorNoise());
230 6a06a912 Leszek Koltunski
       
231
      switch(numPoints)
232
        {
233
        case 0:
234
        case 1: break;
235 291705f6 Leszek Koltunski
        case 2: vc.add(new VectorCache());
236
                vc.add(new VectorCache());
237
                vc.add(new VectorCache());
238 6a06a912 Leszek Koltunski
                break;
239 291705f6 Leszek Koltunski
        default:vc.add(new VectorCache());
240 6a06a912 Leszek Koltunski
        }
241
     
242
      numPoints++;
243
      cacheDirty = true;
244
      }
245
    }
246
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248
/**
249 568b29d8 Leszek Koltunski
 * Adds a new Static2D to the location'th place in our List of Points to interpolate through.
250 6a06a912 Leszek Koltunski
 *   
251
 * @param location Index in our List to add the new Point at.
252
 * @param v The Point to add.
253
 */  
254 568b29d8 Leszek Koltunski
  public synchronized void add(int location, Static2D v)
255 6a06a912 Leszek Koltunski
    {
256
    if( v!=null )
257
      {
258
      vv.add(location, v);
259
      
260 291705f6 Leszek Koltunski
      if( vn!=null ) vn.add(new VectorNoise());
261 6a06a912 Leszek Koltunski
      
262
      switch(numPoints)
263
        {
264
        case 0:
265
        case 1: break;
266 291705f6 Leszek Koltunski
        case 2: vc.add(new VectorCache());
267
                vc.add(new VectorCache());
268
                vc.add(new VectorCache());
269 6a06a912 Leszek Koltunski
                break;
270 291705f6 Leszek Koltunski
        default:vc.add(location,new VectorCache());
271 6a06a912 Leszek Koltunski
        }
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 568b29d8 Leszek Koltunski
  public synchronized boolean remove(Static2D v)
286 6a06a912 Leszek Koltunski
    {
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 1e22c248 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 291705f6 Leszek Koltunski
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
383 1e22c248 Leszek Koltunski
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 6a06a912 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
402 bdb341bc Leszek Koltunski
403 c6dec65b Leszek Koltunski
  synchronized void interpolate(float[] buffer, int offset, float time)
404 6a06a912 Leszek Koltunski
    {
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 291705f6 Leszek Koltunski
417 c0e4d5cf Leszek Koltunski
              int segment= (int)(2*time);
418 291705f6 Leszek Koltunski
419 6a06a912 Leszek Koltunski
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
420 291705f6 Leszek Koltunski
421 6a06a912 Leszek Koltunski
              if( vn!=null )
422
                {
423 c0e4d5cf Leszek Koltunski
                if( segment != mSegment )
424 291705f6 Leszek Koltunski
                  {
425
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
426 c0e4d5cf Leszek Koltunski
                  mSegment = segment;
427 291705f6 Leszek Koltunski
                  }
428
429 6a06a912 Leszek Koltunski
                time = noise(time,0);
430
              
431 663fea68 Leszek Koltunski
                baseV[1][0] = next.x-curr.x;
432
                baseV[1][1] = next.y-curr.y;
433 6a06a912 Leszek Koltunski
   
434 663fea68 Leszek Koltunski
                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 6a06a912 Leszek Koltunski
                }
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 d403b466 Leszek Koltunski
      default:computeSegmentAndTime(time);
445 291705f6 Leszek Koltunski
446 d403b466 Leszek Koltunski
              if( mTmpVec>=0 && mTmpVec<numPoints )
447 291705f6 Leszek Koltunski
                {
448
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
449 d403b466 Leszek Koltunski
                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
450 6a06a912 Leszek Koltunski
                  {
451 d403b466 Leszek Koltunski
                  int vecNext = getNext(mTmpVec,time);
452 6a06a912 Leszek Koltunski
                  next = vv.elementAt(vecNext);
453 9aabc9eb Leszek Koltunski
                  tmpCache2 = vc.elementAt(vecNext);
454 291705f6 Leszek Koltunski
455 9aabc9eb Leszek Koltunski
                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y ) recomputeCache();
456 6a06a912 Leszek Koltunski
                  }
457 649544b8 Leszek Koltunski
458 d403b466 Leszek Koltunski
                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
459 291705f6 Leszek Koltunski
460 d403b466 Leszek Koltunski
                mSegment = mTmpSeg;
461
                time = mTmpTime-mTmpVec;
462
                tmpCache1 = vc.elementAt(mTmpVec);
463 9aabc9eb Leszek Koltunski
                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
464 649544b8 Leszek Koltunski
465 6a06a912 Leszek Koltunski
                if( vn!=null )
466
                  {
467 d403b466 Leszek Koltunski
                  time = noise(time,mTmpVec);
468 649544b8 Leszek Koltunski
469 d403b466 Leszek Koltunski
                  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 6a06a912 Leszek Koltunski
                 
472 9aabc9eb Leszek Koltunski
                  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 6a06a912 Leszek Koltunski
                  } 
475
                else
476
                  {
477 9aabc9eb Leszek Koltunski
                  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 6a06a912 Leszek Koltunski
                  }
480
                
481
                break;
482
                }
483
      }
484
    }  
485
  }
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487
//