Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic2D.java @ 07037b8a

1 d333eb6b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 a4835695 Leszek Koltunski
package org.distorted.library.type;
21 6a06a912 Leszek Koltunski
22
import java.util.Vector;
23
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
/** 
26 568b29d8 Leszek Koltunski
* A 2-dimensional implementation of the Dynamic class to interpolate between a list
27 350cc2f5 Leszek Koltunski
* of Static2Ds.
28 6a06a912 Leszek Koltunski
*/
29
30 568b29d8 Leszek Koltunski
public class Dynamic2D extends Dynamic implements Data2D
31 6a06a912 Leszek Koltunski
  {
32 568b29d8 Leszek Koltunski
  private Vector<Static2D> vv;
33
  private Static2D prev, curr, next;
34 6a06a912 Leszek Koltunski
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36
// no array bounds checking!
37
  
38
  private void vec(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
    tmp1 = 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 649544b8 Leszek Koltunski
        tmp1.tangent[0] = nx+px/q;
63
        tmp1.tangent[1] = ny+py/q;
64 6a06a912 Leszek Koltunski
        }
65
      else
66
        {
67 649544b8 Leszek Koltunski
        tmp1.tangent[0] = px+nx*q;
68
        tmp1.tangent[1] = py+ny*q;
69 6a06a912 Leszek Koltunski
        }
70
      }
71
    else
72
      {
73 649544b8 Leszek Koltunski
      tmp1.tangent[0] = 0.0f;
74
      tmp1.tangent[1] = 0.0f;
75 6a06a912 Leszek Koltunski
      }
76
    }
77
   
78
///////////////////////////////////////////////////////////////////////////////////////////////////
79
  
80
  private void recomputeCache()
81
    {  
82
    if( numPoints==1 )
83
      {
84
      tmp1= vc.elementAt(0);
85
      curr= vv.elementAt(0);
86
              
87 649544b8 Leszek Koltunski
      tmp1.a[0] = tmp1.a[1] = 0.0f;
88
      tmp1.b[0] = tmp1.b[1] = 0.0f;
89
      tmp1.c[0] = curr.x;
90
      tmp1.c[1] = curr.y;
91
      tmp1.d[0] = tmp1.d[1] = 0.0f;
92 6a06a912 Leszek Koltunski
      }
93
    else if( numPoints==2 )
94
      {
95
      tmp1= vc.elementAt(0);
96
      tmp2= vc.elementAt(1);
97
      curr= vv.elementAt(0);
98
      next= vv.elementAt(1);
99
          
100 649544b8 Leszek Koltunski
      tmp1.a[0] = tmp1.a[1] = 0.0f;
101
      tmp1.b[0] = tmp1.b[1] = 0.0f;
102
      tmp1.c[0] = next.x - curr.x;
103
      tmp1.c[1] = next.y - curr.y;
104
      tmp1.d[0] = curr.x;
105
      tmp1.d[1] = curr.y;
106 6a06a912 Leszek Koltunski
      
107 649544b8 Leszek Koltunski
      tmp2.a[0] = tmp2.a[1] = 0.0f;
108
      tmp2.b[0] = tmp2.b[1] = 0.0f;
109
      tmp2.c[0] = curr.x - next.x;
110
      tmp2.c[1] = curr.y - next.y;
111
      tmp2.d[0] = next.x;
112
      tmp2.d[1] = next.y;
113 6a06a912 Leszek Koltunski
      }
114
    else
115
      {
116
      int i, n;  
117
         
118
      for(i=0; i<numPoints; i++) vec(i);
119
   
120
      for(i=0; i<numPoints; i++)
121
        {
122
        n = i<numPoints-1 ? i+1:0;  
123
      
124
        tmp1= vc.elementAt(i);
125
        tmp2= vc.elementAt(n);
126
        curr= vv.elementAt(i);
127
        next= vv.elementAt(n);
128
      
129 649544b8 Leszek Koltunski
        tmp1.cached[0] = curr.x;
130
        tmp1.cached[1] = curr.y;
131
132
        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
133
        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
134
        tmp1.c[0] = tmp1.tangent[0];
135
        tmp1.d[0] = curr.x;
136
137
        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
138
        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
139
        tmp1.c[1] = tmp1.tangent[1];
140
        tmp1.d[1] = curr.y;
141 6a06a912 Leszek Koltunski
        }
142
      }
143
    
144
    cacheDirty = false;
145
    }
146
147
///////////////////////////////////////////////////////////////////////////////////////////////////
148
// PUBLIC API 
149
///////////////////////////////////////////////////////////////////////////////////////////////////
150
/**
151
 * Default constructor.
152
 */
153 568b29d8 Leszek Koltunski
  public Dynamic2D()
154 6a06a912 Leszek Koltunski
    {
155 649544b8 Leszek Koltunski
    super(0,0.5f,2);
156
    vv = new Vector<>();
157 6a06a912 Leszek Koltunski
    }
158 8c893ffc Leszek Koltunski
159
///////////////////////////////////////////////////////////////////////////////////////////////////
160
161
/**
162
 * Default constructor.
163
 *
164
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
165
 *                 last and back to the first
166
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
167
 *                 to the last, back to first, and to the last again.
168
 */
169
  public Dynamic2D(int duration, float count)
170
    {
171 649544b8 Leszek Koltunski
    super(duration,count,2);
172 8c893ffc Leszek Koltunski
    vv = new Vector<>();
173
    }
174
175 6a06a912 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
176
/**
177 568b29d8 Leszek Koltunski
 * Returns the location'th Static2D.
178 6a06a912 Leszek Koltunski
 *   
179
 * @param location the index of the Point we are interested in.
180 568b29d8 Leszek Koltunski
 * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
181 6a06a912 Leszek Koltunski
 */  
182 568b29d8 Leszek Koltunski
  public synchronized Static2D getPoint(int location)
183 6a06a912 Leszek Koltunski
    {
184
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
185
    }
186
  
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188
/**
189
 * Resets the location'th Point.
190
 * 
191
 * @param location the index of the Point we are setting.
192
 * @param x New value of its first float.
193
 */
194
  public synchronized void setPoint(int location, float x, float y)
195
    {
196
    if( location>=0 && location<numPoints )
197
      {
198
      curr = vv.elementAt(location);
199
   
200
      if( curr!=null )
201
        {
202
        curr.set(x,y);
203
        cacheDirty=true;
204
        }
205
      }
206
    }
207
    
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209
/**
210 568b29d8 Leszek Koltunski
 * Adds a new Static2D to the end of our list of Points to interpolate through.
211 6a06a912 Leszek Koltunski
 * <p>   
212
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
213 568b29d8 Leszek Koltunski
 * here, and later on {@link Static2D#set(float,float)} it to some new value and the change
214 6a06a912 Leszek Koltunski
 * will be seamlessly reflected in the interpolated path.  
215
 * <p>
216
 * A Point can be added multiple times.
217
 *   
218
 * @param v The Point to add.
219
 */  
220 568b29d8 Leszek Koltunski
  public synchronized void add(Static2D v)
221 6a06a912 Leszek Koltunski
    {
222
    if( v!=null )
223
      {
224
      vv.add(v);
225
     
226 291705f6 Leszek Koltunski
      if( vn!=null ) vn.add(new VectorNoise());
227 6a06a912 Leszek Koltunski
       
228
      switch(numPoints)
229
        {
230
        case 0:
231
        case 1: break;
232 291705f6 Leszek Koltunski
        case 2: vc.add(new VectorCache());
233
                vc.add(new VectorCache());
234
                vc.add(new VectorCache());
235 6a06a912 Leszek Koltunski
                break;
236 291705f6 Leszek Koltunski
        default:vc.add(new VectorCache());
237 6a06a912 Leszek Koltunski
        }
238
     
239
      numPoints++;
240
      cacheDirty = true;
241
      }
242
    }
243
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245
/**
246 568b29d8 Leszek Koltunski
 * Adds a new Static2D to the location'th place in our List of Points to interpolate through.
247 6a06a912 Leszek Koltunski
 *   
248
 * @param location Index in our List to add the new Point at.
249
 * @param v The Point to add.
250
 */  
251 568b29d8 Leszek Koltunski
  public synchronized void add(int location, Static2D v)
252 6a06a912 Leszek Koltunski
    {
253
    if( v!=null )
254
      {
255
      vv.add(location, v);
256
      
257 291705f6 Leszek Koltunski
      if( vn!=null ) vn.add(new VectorNoise());
258 6a06a912 Leszek Koltunski
      
259
      switch(numPoints)
260
        {
261
        case 0:
262
        case 1: break;
263 291705f6 Leszek Koltunski
        case 2: vc.add(new VectorCache());
264
                vc.add(new VectorCache());
265
                vc.add(new VectorCache());
266 6a06a912 Leszek Koltunski
                break;
267 291705f6 Leszek Koltunski
        default:vc.add(location,new VectorCache());
268 6a06a912 Leszek Koltunski
        }
269
      
270
      numPoints++;
271
      cacheDirty = true;
272
      }
273
    }
274
  
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
/**
277
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
278
 * 
279
 * @param v The Point to remove.
280
 * @return <code>true</code> if we have removed at least one Point.
281
 */
282 568b29d8 Leszek Koltunski
  public synchronized boolean remove(Static2D v)
283 6a06a912 Leszek Koltunski
    {
284
    int n = vv.indexOf(v);
285
    boolean found = false;
286
   
287
    while( n>=0 ) 
288
      {
289
      vv.remove(n);
290
     
291
      if( vn!=null ) vn.remove(0);
292
     
293
      switch(numPoints)
294
        {
295
        case 0:
296
        case 1: 
297
        case 2: break;
298
        case 3: vc.removeAllElements();
299
                break;
300
        default:vc.remove(n);
301
        }
302
     
303
      numPoints--;
304
      found = true;
305
      n = vv.indexOf(v);
306
      }
307
   
308
    if( found ) 
309
      {
310
      cacheDirty=true;
311
      }
312
   
313
    return found;
314
    }
315
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317
/**
318
 * Removes a location'th Point from the List of Points we interpolate through.
319
 * 
320
 * @param location index of the Point we want to remove. 
321
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
322
 */
323
  public synchronized boolean remove(int location)
324
    {
325
    if( location>=0 && location<numPoints ) 
326
      {
327
      vv.removeElementAt(location);
328
      
329
      if( vn!=null ) vn.remove(0);
330
      
331
      switch(numPoints)
332
        {
333
        case 0:
334
        case 1: 
335
        case 2: break;
336
        case 3: vc.removeAllElements();
337
                break;
338
        default:vc.removeElementAt(location);
339
        }
340
341
      numPoints--;
342
      cacheDirty = true; 
343
      return true;
344
      }
345
346
   return false;
347
   }
348
  
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350
/**
351
 * Removes all Points.
352
 */
353
  public synchronized void removeAll()
354
    {
355
    numPoints = 0;
356
    vv.removeAllElements();
357
    vc.removeAllElements();
358
    cacheDirty = false;
359
   
360
    if( vn!=null ) vn.removeAllElements();
361
    }
362
363 1e22c248 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
364
/**
365
 * Sets the 'smoothness' of interpolation.
366
 * <p>
367
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
368
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
369
 * up and slowing down, etc.
370
 *
371
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
372
 */
373
374
  public synchronized void setNoise(Static2D noise)
375
    {
376
    if( vn==null )
377
      {
378
      vn = new Vector<>();
379 291705f6 Leszek Koltunski
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
380 1e22c248 Leszek Koltunski
381
      if( mDimension>=2 )
382
        {
383
        mFactor = new float[mDimension-1];
384
        }
385
386
      mNoise = new float[mDimension];
387
      }
388
389
    if( noise.x<0.0f ) noise.x = 0.0f;
390
    if( noise.x>1.0f ) noise.x = 1.0f;
391
    if( noise.y<0.0f ) noise.y = 0.0f;
392
    if( noise.y>1.0f ) noise.y = 1.0f;
393
394
    mNoise[0] = noise.x;
395
    mNoise[1] = noise.y;
396
    }
397
398 6a06a912 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
399 bdb341bc Leszek Koltunski
400 c6dec65b Leszek Koltunski
  synchronized void interpolate(float[] buffer, int offset, float time)
401 6a06a912 Leszek Koltunski
    {
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 291705f6 Leszek Koltunski
414
              int segment2= (int)(2*time);
415
416 6a06a912 Leszek Koltunski
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
417 291705f6 Leszek Koltunski
418 6a06a912 Leszek Koltunski
              if( vn!=null )
419
                {
420 291705f6 Leszek Koltunski
                if( segment2 != mSegment )
421
                  {
422
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
423
                  mSegment = segment2;
424
                  }
425
426 6a06a912 Leszek Koltunski
                time = noise(time,0);
427
              
428 663fea68 Leszek Koltunski
                baseV[1][0] = next.x-curr.x;
429
                baseV[1][1] = next.y-curr.y;
430 6a06a912 Leszek Koltunski
   
431 663fea68 Leszek Koltunski
                buffer[offset  ] = baseV[1][0]*time + curr.x +baseV[1][1]*mFactor[0];
432
                buffer[offset+1] = baseV[1][1]*time + curr.y -baseV[1][0]*mFactor[0];
433 6a06a912 Leszek Koltunski
                }
434
              else
435
                {
436
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
437
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
438
                }
439
              
440
              break;
441
      default:float t = time;
442 291705f6 Leszek Koltunski
              int vecCurr, segment;
443
444 6a06a912 Leszek Koltunski
              switch(mMode)
445
                {
446
                case MODE_LOOP: time = time*numPoints;
447 291705f6 Leszek Koltunski
                                segment = (int)time;
448
                                vecCurr = segment;
449 6a06a912 Leszek Koltunski
                                break;
450 291705f6 Leszek Koltunski
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
451
452 65d5505f Leszek Koltunski
                                if( t<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
453
                                  {            // and end position is slightly not equal to the end point => might not get autodeleted!
454 291705f6 Leszek Koltunski
                                  time = 2*t*(numPoints-1);
455
                                  vecCurr = segment;
456
                                  }
457
                                else
458
                                  {
459
                                  time = 2*(1-t)*(numPoints-1);
460
                                  vecCurr = 2*numPoints-3-segment;
461
                                  }
462 6a06a912 Leszek Koltunski
                                break;
463
                case MODE_JUMP: time = time*(numPoints-1);
464 291705f6 Leszek Koltunski
                                segment = (int)time;
465
                                vecCurr = segment;
466 6a06a912 Leszek Koltunski
                                break;
467 291705f6 Leszek Koltunski
                default       : vecCurr = 0;
468
                                segment = 0;
469 6a06a912 Leszek Koltunski
                }
470 291705f6 Leszek Koltunski
471 6a06a912 Leszek Koltunski
              if( vecCurr>=0 && vecCurr<numPoints )
472 291705f6 Leszek Koltunski
                {
473
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
474
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
475 6a06a912 Leszek Koltunski
                  {
476 291705f6 Leszek Koltunski
                  int vecNext;
477
478 6a06a912 Leszek Koltunski
                  switch(mMode)
479
                    {
480 291705f6 Leszek Koltunski
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
481 6a06a912 Leszek Koltunski
                                    break;
482 65d5505f Leszek Koltunski
                    case MODE_PATH: if( t<=0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
483
                                    else          vecNext = vecCurr==0 ? 1 : vecCurr-1;
484 6a06a912 Leszek Koltunski
                                    break;
485
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
486
                                    break;
487 291705f6 Leszek Koltunski
                    default       : vecNext = 0;
488 6a06a912 Leszek Koltunski
                    }
489 291705f6 Leszek Koltunski
490 6a06a912 Leszek Koltunski
                  next = vv.elementAt(vecNext);
491
                  tmp2 = vc.elementAt(vecNext);
492 291705f6 Leszek Koltunski
493 75ec369a Leszek Koltunski
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y ) recomputeCache();
494 6a06a912 Leszek Koltunski
                  }
495 649544b8 Leszek Koltunski
496 291705f6 Leszek Koltunski
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
497
498
                mSegment = segment;
499
500
                time = time-vecCurr;
501 649544b8 Leszek Koltunski
                tmp1 = vc.elementAt(vecCurr);
502
503 6a06a912 Leszek Koltunski
                if( vn!=null )
504
                  {
505
                  time = noise(time,vecCurr);
506 649544b8 Leszek Koltunski
507 663fea68 Leszek Koltunski
                  baseV[1][0] = (3*tmp1.a[0]*time+2*tmp1.b[0])*time + tmp1.c[0];
508
                  baseV[1][1] = (3*tmp1.a[1]*time+2*tmp1.b[1])*time + tmp1.c[1];
509 6a06a912 Leszek Koltunski
                 
510 65a21c19 Leszek Koltunski
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] +baseV[1][1]*mFactor[0];
511
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] -baseV[1][0]*mFactor[0];
512 6a06a912 Leszek Koltunski
                  } 
513
                else
514
                  {
515 649544b8 Leszek Koltunski
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
516
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
517 6a06a912 Leszek Koltunski
                  }
518
                
519
                break;
520
                }
521
      }
522
    }  
523
  }
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525
//