Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic2D.java @ 3002bef3

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

    
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
// the coefficients of the X(t), Y(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
35
// (x,y) is the vector tangent to the path.
36
// (vx,vy) is the original vector from vv (copied here so when interpolating we can see if it is 
37
// still valid and if not - rebuild the Cache
38
  
39
  private class VectorCache
40
    {
41
    float ax, bx, cx, dx;
42
    float ay, by, cy, dy;
43
   
44
    float x,y;
45
    float vx,vy;
46
    }
47

    
48
  private Vector<VectorCache> vc;
49
  private VectorCache tmp1, tmp2;
50
   
51
  private Vector<Static2D> vv;
52
  private Static2D prev, curr, next;
53

    
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55
// no array bounds checking!
56
  
57
  private void vec(int c)
58
    {
59
    int p = c>0 ? c-1: numPoints-1;
60
    int n = c<numPoints-1 ? c+1: 0;
61
    
62
    prev = vv.elementAt(p);
63
    curr = vv.elementAt(c);
64
    next = vv.elementAt(n);
65

    
66
    tmp1 = vc.elementAt(c);
67
    
68
    float px = curr.x - prev.x;
69
    float py = curr.y - prev.y;
70
    float nx = next.x - curr.x;
71
    float ny = next.y - curr.y;
72
     
73
    float d = nx*nx+ny*ny;
74
    
75
    if( d>0 )
76
      {
77
      float q = (float)Math.sqrt((px*px+py*py)/d);
78
      
79
      if( q>1 )
80
        {
81
        tmp1.x = nx+px/q;
82
        tmp1.y = ny+py/q;
83
        }
84
      else
85
        {
86
        tmp1.x = px+nx*q;
87
        tmp1.y = py+ny*q;
88
        }
89
      }
90
    else
91
      {
92
      tmp1.x = 0.0f;
93
      tmp1.y = 0.0f;
94
      }
95
    }
96
   
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98
  
99
  private void recomputeCache()
100
    {  
101
    if( numPoints==1 )
102
      {
103
      tmp1= vc.elementAt(0);
104
      curr= vv.elementAt(0);
105
              
106
      tmp1.ax = tmp1.ay = 0.0f;
107
      tmp1.bx = tmp1.by = 0.0f;
108
      tmp1.cx = curr.x;
109
      tmp1.cy = curr.y;
110
      tmp1.dx = tmp1.dy = 0.0f;
111
      }
112
    else if( numPoints==2 )
113
      {
114
      tmp1= vc.elementAt(0);
115
      tmp2= vc.elementAt(1);
116
      curr= vv.elementAt(0);
117
      next= vv.elementAt(1);
118
          
119
      tmp1.ax = tmp1.ay = 0.0f;
120
      tmp1.bx = tmp1.by = 0.0f;
121
      tmp1.cx = next.x - curr.x;
122
      tmp1.cy = next.y - curr.y;
123
      tmp1.dx = curr.x;
124
      tmp1.dy = curr.y;
125
      
126
      tmp2.ax = tmp2.ay = 0.0f;
127
      tmp2.bx = tmp2.by = 0.0f;
128
      tmp2.cx = curr.x - next.x;
129
      tmp2.cy = curr.y - next.y;
130
      tmp2.dx = next.x;
131
      tmp2.dy = next.y;
132
      }
133
    else
134
      {
135
      int i, n;  
136
         
137
      for(i=0; i<numPoints; i++) vec(i);
138
   
139
      for(i=0; i<numPoints; i++)
140
        {
141
        n = i<numPoints-1 ? i+1:0;  
142
      
143
        tmp1= vc.elementAt(i);
144
        tmp2= vc.elementAt(n);
145
        curr= vv.elementAt(i);
146
        next= vv.elementAt(n);
147
      
148
        tmp1.vx = curr.x;
149
        tmp1.vy = curr.y;
150
        
151
        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
152
        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
153
        tmp1.cx = tmp1.x;
154
        tmp1.dx = curr.x;
155
      
156
        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
157
        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
158
        tmp1.cy = tmp1.y;
159
        tmp1.dy = curr.y;
160
        }
161
      }
162
    
163
    cacheDirty = false;
164
    }
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167
// PUBLIC API 
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169
/**
170
 * Default constructor.
171
 */
172
  public Dynamic2D()
173
    {
174
    this(0,0.5f);
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

    
179
/**
180
 * Default constructor.
181
 *
182
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
183
 *                 last and back to the first
184
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
185
 *                 to the last, back to first, and to the last again.
186
 */
187
  public Dynamic2D(int duration, float count)
188
    {
189
    vv = new Vector<>();
190
    vc = new Vector<>();
191
    vn = null;
192
    numPoints = 0;
193
    cacheDirty = false;
194
    mMode = MODE_LOOP;
195
    mDuration = duration;
196
    mCount = count;
197
    mDimension = 2;
198
    }
199

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201
/**
202
 * Returns the location'th Static2D.
203
 *   
204
 * @param location the index of the Point we are interested in.
205
 * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
206
 */  
207
  public synchronized Static2D getPoint(int location)
208
    {
209
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
210
    }
211
  
212
///////////////////////////////////////////////////////////////////////////////////////////////////
213
/**
214
 * Resets the location'th Point.
215
 * 
216
 * @param location the index of the Point we are setting.
217
 * @param x New value of its first float.
218
 */
219
  public synchronized void setPoint(int location, float x, float y)
220
    {
221
    if( location>=0 && location<numPoints )
222
      {
223
      curr = vv.elementAt(location);
224
   
225
      if( curr!=null )
226
        {
227
        curr.set(x,y);
228
        cacheDirty=true;
229
        }
230
      }
231
    }
232
    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234
/**
235
 * Adds a new Static2D to the end of our list of Points to interpolate through.
236
 * <p>   
237
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
238
 * here, and later on {@link Static2D#set(float,float)} it to some new value and the change
239
 * will be seamlessly reflected in the interpolated path.  
240
 * <p>
241
 * A Point can be added multiple times.
242
 *   
243
 * @param v The Point to add.
244
 */  
245
  public synchronized void add(Static2D v)
246
    {
247
    if( v!=null )
248
      {
249
      vv.add(v);
250
     
251
      if( vn!=null ) vn.add(new VectorNoise(2));
252
       
253
      switch(numPoints)
254
        {
255
        case 0:
256
        case 1: break;
257
        case 2: vc.add(new VectorCache());
258
                vc.add(new VectorCache());
259
                vc.add(new VectorCache());
260
                break;
261
        default:vc.add(new VectorCache());
262
        }
263
     
264
      numPoints++;
265
      cacheDirty = true;
266
      }
267
    }
268

    
269
///////////////////////////////////////////////////////////////////////////////////////////////////
270
/**
271
 * Adds a new Static2D to the location'th place in our List of Points to interpolate through.
272
 *   
273
 * @param location Index in our List to add the new Point at.
274
 * @param v The Point to add.
275
 */  
276
  public synchronized void add(int location, Static2D v)
277
    {
278
    if( v!=null )
279
      {
280
      vv.add(location, v);
281
      
282
      if( vn!=null ) vn.add(new VectorNoise(2));
283
      
284
      switch(numPoints)
285
        {
286
        case 0:
287
        case 1: break;
288
        case 2: vc.add(new VectorCache());
289
                vc.add(new VectorCache());
290
                vc.add(new VectorCache());
291
                break;
292
        default:vc.add(location,new VectorCache());
293
        }
294
      
295
      numPoints++;
296
      cacheDirty = true;
297
      }
298
    }
299
  
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
/**
302
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
303
 * 
304
 * @param v The Point to remove.
305
 * @return <code>true</code> if we have removed at least one Point.
306
 */
307
  public synchronized boolean remove(Static2D v)
308
    {
309
    int n = vv.indexOf(v);
310
    boolean found = false;
311
   
312
    while( n>=0 ) 
313
      {
314
      vv.remove(n);
315
     
316
      if( vn!=null ) vn.remove(0);
317
     
318
      switch(numPoints)
319
        {
320
        case 0:
321
        case 1: 
322
        case 2: break;
323
        case 3: vc.removeAllElements();
324
                break;
325
        default:vc.remove(n);
326
        }
327
     
328
      numPoints--;
329
      found = true;
330
      n = vv.indexOf(v);
331
      }
332
   
333
    if( found ) 
334
      {
335
      cacheDirty=true;
336
      }
337
   
338
    return found;
339
    }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342
/**
343
 * Removes a location'th Point from the List of Points we interpolate through.
344
 * 
345
 * @param location index of the Point we want to remove. 
346
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
347
 */
348
  public synchronized boolean remove(int location)
349
    {
350
    if( location>=0 && location<numPoints ) 
351
      {
352
      vv.removeElementAt(location);
353
      
354
      if( vn!=null ) vn.remove(0);
355
      
356
      switch(numPoints)
357
        {
358
        case 0:
359
        case 1: 
360
        case 2: break;
361
        case 3: vc.removeAllElements();
362
                break;
363
        default:vc.removeElementAt(location);
364
        }
365

    
366
      numPoints--;
367
      cacheDirty = true; 
368
      return true;
369
      }
370

    
371
   return false;
372
   }
373
  
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375
/**
376
 * Removes all Points.
377
 */
378
  public synchronized void removeAll()
379
    {
380
    numPoints = 0;
381
    vv.removeAllElements();
382
    vc.removeAllElements();
383
    cacheDirty = false;
384
   
385
    if( vn!=null ) vn.removeAllElements();
386
    }
387

    
388
///////////////////////////////////////////////////////////////////////////////////////////////////
389
/**
390
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
391
 * <p>
392
 * Since this is a 2-dimensional Dynamic, the resulting interpolated Static2D gets written
393
 * to two locations in the buffer: buffer[offset] and buffer[offset+1]. 
394
 * 
395
 * @param buffer Float buffer we will write the resulting Static2D to.
396
 * @param offset Offset in the buffer where to write the result.
397
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
398
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
399
 */  
400
  public synchronized void interpolate(float[] buffer, int offset, float time)
401
    {
402
    switch(numPoints)
403
      {
404
      case 0: buffer[offset  ] = 0.0f;
405
              buffer[offset+1] = 0.0f;
406
              break;
407
      case 1: curr = vv.elementAt(0);
408
              buffer[offset  ] = curr.x;
409
              buffer[offset+1] = curr.y;
410
              break;
411
      case 2: curr = vv.elementAt(0);
412
              next = vv.elementAt(1);
413
               
414
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
415
             
416
              if( vn!=null )
417
                {
418
                time = noise(time,0);
419
              
420
                float dx2 = next.x-curr.x;
421
                float dy2 = next.y-curr.y;
422
   
423
                buffer[offset  ] = dx2*time + curr.x +dy2*mFactor[0];
424
                buffer[offset+1] = dy2*time + curr.y -dx2*mFactor[0];
425
                }
426
              else
427
                {
428
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
429
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
430
                }
431
              
432
              break;
433
      default:float t = time;
434
        
435
              switch(mMode)
436
                {
437
                case MODE_LOOP: time = time*numPoints;
438
                                break;
439
                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
440
                                break;
441
                case MODE_JUMP: time = time*(numPoints-1);
442
                                break;
443
                }
444
            
445
              int vecCurr = (int)time;
446
              time = time-vecCurr;
447
      
448
              if( vecCurr>=0 && vecCurr<numPoints )
449
                { 
450
                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
451
                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
452
                  {
453
                  int vecNext;   
454
                  mVecCurr = vecCurr;
455
                                
456
                  switch(mMode)
457
                    {
458
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
459
                                    break;
460
                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
461
                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
462
                                    break;
463
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
464
                                    break;
465
                    default       : vecNext = 0;                
466
                    }
467
              
468
                  next = vv.elementAt(vecNext);
469
                  tmp2 = vc.elementAt(vecNext);
470
              
471
                  if( tmp2.vx!=next.x || tmp2.vy!=next.y ) recomputeCache();
472
                  }
473
              
474
                if( vn!=null )
475
                  {
476
                  time = noise(time,vecCurr);
477
                  tmp1 = vc.elementAt(vecCurr);
478
               
479
                  float dx2 = (3*tmp1.ax*time+2*tmp1.bx)*time + tmp1.cx;
480
                  float dy2 = (3*tmp1.ay*time+2*tmp1.by)*time + tmp1.cy;
481
                 
482
                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx +dy2*mFactor[0];
483
                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy -dx2*mFactor[0];
484
                  } 
485
                else
486
                  {
487
                  tmp1 = vc.elementAt(vecCurr);
488
                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
489
                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
490
                  }
491
                
492
                break;
493
                }
494
      }
495
    }  
496
  }
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498
//
(8-8/17)