Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic2D.java @ 43b28f5b

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
    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
        tmp1.tangent[0] = nx+px/q;
63
        tmp1.tangent[1] = ny+py/q;
64
        }
65
      else
66
        {
67
        tmp1.tangent[0] = px+nx*q;
68
        tmp1.tangent[1] = py+ny*q;
69
        }
70
      }
71
    else
72
      {
73
      tmp1.tangent[0] = 0.0f;
74
      tmp1.tangent[1] = 0.0f;
75
      }
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
      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
      }
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
      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
      
107
      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
      }
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
        tmp1= vc.elementAt(i);
125
        tmp2= vc.elementAt(n);
126
        curr= vv.elementAt(i);
127
        next= vv.elementAt(n);
128
      
129
        tmp1.cached[0] = curr.x;
130
        tmp1.cached[1] = curr.y;
131

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

    
137
        tmp1.a[1] = mConvexity*( 2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1]);
138
        tmp1.b[1] = mConvexity*(-3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1]);
139
        tmp1.c[1] = mConvexity*(tmp1.tangent[1]) + (1.0f-mConvexity)*(next.y-curr.y);
140
        tmp1.d[1] = curr.y;
141
        }
142
      }
143
    
144
    cacheDirty = false;
145
    }
146

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

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

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176
/**
177
 * Returns the location'th Static2D.
178
 *   
179
 * @param location the index of the Point we are interested in.
180
 * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
181
 */  
182
  public synchronized Static2D getPoint(int location)
183
    {
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
 * Adds a new Static2D to the end of our list of Points to interpolate through.
211
 * <p>   
212
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
213
 * here, and later on {@link Static2D#set(float,float)} it to some new value and the change
214
 * 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
  public synchronized void add(Static2D v)
221
    {
222
    if( v!=null )
223
      {
224
      vv.add(v);
225
     
226
      if( vn!=null ) vn.add(new VectorNoise());
227
       
228
      switch(numPoints)
229
        {
230
        case 0:
231
        case 1: break;
232
        case 2: vc.add(new VectorCache());
233
                vc.add(new VectorCache());
234
                vc.add(new VectorCache());
235
                break;
236
        default:vc.add(new VectorCache());
237
        }
238
     
239
      numPoints++;
240
      cacheDirty = true;
241
      }
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245
/**
246
 * Adds a new Static2D to the location'th place in our List of Points to interpolate through.
247
 *   
248
 * @param location Index in our List to add the new Point at.
249
 * @param v The Point to add.
250
 */  
251
  public synchronized void add(int location, Static2D v)
252
    {
253
    if( v!=null )
254
      {
255
      vv.add(location, v);
256
      
257
      if( vn!=null ) vn.add(new VectorNoise());
258
      
259
      switch(numPoints)
260
        {
261
        case 0:
262
        case 1: break;
263
        case 2: vc.add(new VectorCache());
264
                vc.add(new VectorCache());
265
                vc.add(new VectorCache());
266
                break;
267
        default:vc.add(location,new VectorCache());
268
        }
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
  public synchronized boolean remove(Static2D v)
283
    {
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
///////////////////////////////////////////////////////////////////////////////////////////////////
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
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
380

    
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
///////////////////////////////////////////////////////////////////////////////////////////////////
399

    
400
  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
              int segment2= (int)(2*time);
415

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

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

    
426
                time = noise(time,0);
427
              
428
                baseV[1][0] = next.x-curr.x;
429
                baseV[1][1] = next.y-curr.y;
430
   
431
                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
                }
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
              int vecCurr, segment;
443

    
444
              switch(mMode)
445
                {
446
                case MODE_LOOP: time = time*numPoints;
447
                                segment = (int)time;
448
                                vecCurr = segment;
449
                                break;
450
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
451

    
452
                                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
                                  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
                                break;
463
                case MODE_JUMP: time = time*(numPoints-1);
464
                                segment = (int)time;
465
                                vecCurr = segment;
466
                                break;
467
                default       : vecCurr = 0;
468
                                segment = 0;
469
                }
470

    
471
              if( vecCurr>=0 && vecCurr<numPoints )
472
                {
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
                  {
476
                  int vecNext = getNext(vecCurr,t);
477
                  next = vv.elementAt(vecNext);
478
                  tmp2 = vc.elementAt(vecNext);
479

    
480
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y ) recomputeCache();
481
                  }
482

    
483
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
484

    
485
                mSegment = segment;
486

    
487
                time = time-vecCurr;
488
                tmp1 = vc.elementAt(vecCurr);
489

    
490
                if( vn!=null )
491
                  {
492
                  time = noise(time,vecCurr);
493

    
494
                  baseV[1][0] = (3*tmp1.a[0]*time+2*tmp1.b[0])*time + tmp1.c[0];
495
                  baseV[1][1] = (3*tmp1.a[1]*time+2*tmp1.b[1])*time + tmp1.c[1];
496
                 
497
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] +baseV[1][1]*mFactor[0];
498
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] -baseV[1][0]*mFactor[0];
499
                  } 
500
                else
501
                  {
502
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
503
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
504
                  }
505
                
506
                break;
507
                }
508
      }
509
    }  
510
  }
511
///////////////////////////////////////////////////////////////////////////////////////////////////
512
//
(8-8/18)