Project

General

Profile

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

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

1
////////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// 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
//                                                                                               //
11
// This library 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 GNU                             //
14
// Lesser General Public License for more details.                                               //
15
//                                                                                               //
16
// 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
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
package org.distorted.library.type;
22

    
23
import java.util.Vector;
24

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26
/** 
27
* A 4-dimensional implementation of the Dynamic class to interpolate between a list
28
* of Static4Ds.
29
*/
30

    
31
public class Dynamic4D extends Dynamic implements Data4D
32
  {
33
  private Vector<Static4D> vv;
34
  private Static4D prev, curr, next;
35

    
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37
// no array bounds checking!
38
  
39
  private void computeVelocity(int c)
40
    {
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
    tmpCache1 = vc.elementAt(c);
49
    
50
    float px = curr.x - prev.x;
51
    float py = curr.y - prev.y;
52
    float pz = curr.z - prev.z;
53
    float pw = curr.w - prev.w;
54
    float nx = next.x - curr.x;
55
    float ny = next.y - curr.y;
56
    float nz = next.z - curr.z;
57
    float nw = next.w - curr.w;
58
     
59
    float d = nx*nx+ny*ny+nz*nz+nw*nw;
60
    
61
    if( d>0 )
62
      {
63
      float q = (float)Math.sqrt((px*px+py*py+pz*pz+pw*pw)/d);
64
      
65
      if( q>1 )
66
        {
67
        tmpCache1.velocity[0] = nx+px/q;
68
        tmpCache1.velocity[1] = ny+py/q;
69
        tmpCache1.velocity[2] = nz+pz/q;
70
        tmpCache1.velocity[3] = nw+pw/q;
71
        }
72
      else
73
        {
74
        tmpCache1.velocity[0] = px+nx*q;
75
        tmpCache1.velocity[1] = py+ny*q;
76
        tmpCache1.velocity[2] = pz+nz*q;
77
        tmpCache1.velocity[3] = pw+nw*q;
78
        }
79
      }
80
    else
81
      {
82
      tmpCache1.velocity[0] = 0.0f;
83
      tmpCache1.velocity[1] = 0.0f;
84
      tmpCache1.velocity[2] = 0.0f;
85
      tmpCache1.velocity[3] = 0.0f;
86
      }
87
    }
88
    
89
///////////////////////////////////////////////////////////////////////////////////////////////////
90
  
91
  private void recomputeCache()
92
    {  
93
    if( numPoints==1 )
94
      {
95
      tmpCache1 = vc.elementAt(0);
96
      curr= vv.elementAt(0);
97
        
98
      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
99
      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
100
      tmpCache1.c[0] = curr.x;
101
      tmpCache1.c[1] = curr.y;
102
      tmpCache1.c[2] = curr.z;
103
      tmpCache1.c[3] = curr.w;
104
      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[3] = tmpCache1.d[3] = 0.0f;
105
      }
106
    else if( numPoints==2 )
107
      {
108
      tmpCache1 = vc.elementAt(0);
109
      tmpCache2 = vc.elementAt(1);
110
      curr= vv.elementAt(0);
111
      next= vv.elementAt(1);
112
      
113
      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
114
      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
115
      tmpCache1.c[0] = next.x - curr.x;
116
      tmpCache1.c[1] = next.y - curr.y;
117
      tmpCache1.c[2] = next.z - curr.z;
118
      tmpCache1.c[3] = next.w - curr.w;
119
      tmpCache1.d[0] = curr.x;
120
      tmpCache1.d[1] = curr.y;
121
      tmpCache1.d[2] = curr.z;
122
      tmpCache1.d[3] = curr.w;
123
      
124
      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = tmpCache2.a[3] = 0.0f;
125
      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = tmpCache2.b[3] = 0.0f;
126
      tmpCache2.c[0] = curr.x - next.x;
127
      tmpCache2.c[1] = curr.y - next.y;
128
      tmpCache2.c[2] = curr.z - next.z;
129
      tmpCache2.c[3] = curr.w - next.w;
130
      tmpCache2.d[0] = next.x;
131
      tmpCache2.d[1] = next.y;
132
      tmpCache2.d[2] = next.z;
133
      tmpCache2.d[3] = next.w;
134
      }
135
    else
136
      {
137
      int i, n;  
138
      
139
      for(i=0; i<numPoints; i++) computeVelocity(i);
140
   
141
      for(i=0; i<numPoints; i++)
142
        {
143
        n = i<numPoints-1 ? i+1:0;  
144
      
145
        tmpCache1 = vc.elementAt(i);
146
        tmpCache2 = vc.elementAt(n);
147
        curr= vv.elementAt(i);
148
        next= vv.elementAt(n);
149
      
150
        tmpCache1.cached[0] = curr.x;
151
        tmpCache1.cached[1] = curr.y;
152
        tmpCache1.cached[2] = curr.z;
153
        tmpCache1.cached[3] = curr.w;
154
        
155
        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
156
        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
157
        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
158
        tmpCache1.d[0] = curr.x;
159
      
160
        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
161
        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
162
        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
163
        tmpCache1.d[1] = curr.y;
164
      
165
        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
166
        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
167
        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
168
        tmpCache1.d[2] = curr.z;
169
        
170
        tmpCache1.a[3] = mConvexity*( 2*curr.w +   tmpCache1.velocity[3] - 2*next.w + tmpCache2.velocity[3]);
171
        tmpCache1.b[3] = mConvexity*(-3*curr.w - 2* tmpCache1.velocity[3] + 3*next.w - tmpCache2.velocity[3]);
172
        tmpCache1.c[3] = mConvexity*(tmpCache1.velocity[3]) + (1.0f-mConvexity)*(next.w-curr.w);
173
        tmpCache1.d[3] = curr.w;
174

    
175
        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
176
        }
177
      }
178
   
179
    cacheDirty = false;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183
// PUBLIC API
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185
/**
186
 * Default constructor.
187
 */
188
  public Dynamic4D()
189
    {
190
    super(0,0.5f,4);
191
    vv = new Vector<>();
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195
/**
196
 * Constructor setting the speed of interpolation and the number of revolutions.
197
 *
198
 * What constitutes 'one revolution' depends on the MODE:
199
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
200
 *
201
 * @param duration number of milliseconds it takes to do one revolution.
202
 * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
203
 */
204
  public Dynamic4D(int duration, float count)
205
    {
206
    super(duration,count,4);
207
    vv = new Vector<>();
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211
/**
212
 * Returns the location'th Static4D.
213
 *   
214
 * @param location the index of the Point we are interested in.
215
 * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
216
 */  
217
  public synchronized Static4D getPoint(int location)
218
    {
219
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
220
    }
221
  
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223
/**
224
 * Resets the location'th Point.
225
 * 
226
 * @param location the index of the Point we are setting.
227
 * @param x New value of its first float.
228
 */
229
  public synchronized void setPoint(int location, float x, float y, float z, float w)
230
    {
231
    if( location>=0 && location<numPoints )
232
      {
233
      curr = vv.elementAt(location);
234
   
235
      if( curr!=null )
236
        {
237
        curr.set(x,y,z,w);
238
        cacheDirty=true;
239
        }
240
      }
241
    }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244
/**
245
 * Adds a new Static4D to the end of our list of Points to interpolate through.
246
 * <p>   
247
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
248
 * here, and later on {@link Static4D#set(float,float,float,float)} it to some new value and
249
 * the change will be seamlessly reflected in the interpolated path.  
250
 * <p>
251
 * A Point can be added multiple times.
252
 *   
253
 * @param v The Point to add.
254
 */    
255
  public synchronized void add(Static4D v)
256
    {
257
    if( v!=null )
258
      {
259
      vv.add(v);
260
        
261
      if( vn!=null ) vn.add(new VectorNoise());
262
       
263
      switch(numPoints)
264
        {
265
        case 0: break;
266
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
267
                break;
268
        case 2: vc.add(new VectorCache());
269
                vc.add(new VectorCache());
270
                vc.add(new VectorCache());
271
                break;
272
        default:vc.add(new VectorCache());
273
        }
274

    
275
      numPoints++;
276
      cacheDirty = true;
277
      }
278
    }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
/**
282
 * Adds a new Static4D to the location'th place in our List of Points to interpolate through.
283
 *   
284
 * @param location Index in our List to add the new Point at.
285
 * @param v The Static4D to add.
286
 */  
287
  public synchronized void add(int location, Static4D v)
288
    {
289
    if( v!=null )
290
      {
291
      vv.add(location, v);
292
      
293
      if( vn!=null ) vn.add(new VectorNoise());
294
      
295
      switch(numPoints)
296
        {
297
        case 0: break;
298
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
299
                break;
300
        case 2: vc.add(new VectorCache());
301
                vc.add(new VectorCache());
302
                vc.add(new VectorCache());
303
                break;
304
        default:vc.add(location,new VectorCache());
305
        }
306

    
307
      numPoints++;
308
      cacheDirty = true;
309
      }
310
    }
311
  
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313
/**
314
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
315
 * 
316
 * @param v The Point to remove.
317
 * @return <code>true</code> if we have removed at least one Point.
318
 */
319
  public synchronized boolean remove(Static4D v)
320
    {
321
    int n = vv.indexOf(v);
322
    boolean found = false;
323
   
324
    while( n>=0 ) 
325
      {
326
      vv.remove(n);
327
     
328
      if( vn!=null ) vn.remove(0);
329
     
330
      switch(numPoints)
331
        {
332
        case 0:
333
        case 1: 
334
        case 2: break;
335
        case 3: vc.removeAllElements();
336
                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
337
                break;
338
        default:vc.remove(n);
339
        }
340

    
341
      numPoints--;
342
      found = true;
343
      n = vv.indexOf(v);
344
      }
345
   
346
    if( found ) 
347
      {
348
      cacheDirty=true;
349
      }
350
   
351
    return found;
352
    }
353

    
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355
/**
356
 * Removes a location'th Point from the List of Points we interpolate through.
357
 * 
358
 * @param location index of the Point we want to remove. 
359
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
360
 */
361
  public synchronized boolean remove(int location)
362
    {
363
    if( location>=0 && location<numPoints ) 
364
      {
365
      vv.removeElementAt(location);
366
       
367
      if( vn!=null ) vn.remove(0);
368
      
369
      switch(numPoints)
370
        {
371
        case 0:
372
        case 1: 
373
        case 2: break;
374
        case 3: vc.removeAllElements();
375
                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
376
                break;
377
        default:vc.removeElementAt(location);
378
        }
379

    
380
      numPoints--;
381
      cacheDirty = true; 
382
      return true;
383
      }
384

    
385
    return false;
386
    }
387
  
388
///////////////////////////////////////////////////////////////////////////////////////////////////
389
/**
390
 * Removes all Points.
391
 */
392
  public synchronized void removeAll()
393
    {
394
    numPoints = 0;
395
    vv.removeAllElements();
396
    vc.removeAllElements();
397
    cacheDirty = false;
398
   
399
    if( vn!=null ) vn.removeAllElements();
400
    }
401

    
402
///////////////////////////////////////////////////////////////////////////////////////////////////
403
/**
404
 * Sets the 'smoothness' of interpolation.
405
 * <p>
406
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
407
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
408
 * up and slowing down, etc.
409
 *
410
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
411
 */
412

    
413
  public synchronized void setNoise(Static4D noise)
414
    {
415
    if( vn==null )
416
      {
417
      vn = new Vector<>();
418
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
419

    
420
      if( mDimension>=2 )
421
        {
422
        mFactor = new float[mDimension-1];
423
        }
424

    
425
      mNoise = new float[mDimension];
426
      }
427

    
428
    if( noise.x<0.0f ) noise.x = 0.0f;
429
    if( noise.x>1.0f ) noise.x = 1.0f;
430
    if( noise.y<0.0f ) noise.y = 0.0f;
431
    if( noise.y>1.0f ) noise.y = 1.0f;
432
    if( noise.z<0.0f ) noise.z = 0.0f;
433
    if( noise.z>1.0f ) noise.z = 1.0f;
434
    if( noise.w<0.0f ) noise.w = 0.0f;
435
    if( noise.w>1.0f ) noise.w = 1.0f;
436

    
437
    mNoise[0] = noise.x;
438
    mNoise[1] = noise.y;
439
    mNoise[2] = noise.z;
440
    mNoise[3] = noise.w;
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444

    
445
  synchronized void interpolate(float[] buffer, int offset, float time)
446
    {  
447
    switch(numPoints)
448
      {
449
      case 0: buffer[offset  ] = 0.0f;
450
              buffer[offset+1] = 0.0f;
451
              buffer[offset+2] = 0.0f;
452
              buffer[offset+3] = 0.0f;
453
              break;
454
      case 1: curr = vv.elementAt(0);
455
              buffer[offset  ] = curr.x;
456
              buffer[offset+1] = curr.y;
457
              buffer[offset+2] = curr.z;
458
              buffer[offset+3] = curr.w;
459
              break;
460
      case 2: curr = vv.elementAt(0);
461
              next = vv.elementAt(1);
462

    
463
              int segment= (int)(2*time);
464

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

    
467
              if( vn!=null )
468
                {
469
                if( segment != mSegment )
470
                  {
471
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
472
                  mSegment = segment;
473
                  }
474

    
475
                time = noise(time,0);
476

    
477
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
478
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
479
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
480
                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
481
                }
482
              else
483
                {
484
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
485
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
486
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
487
                buffer[offset+3] = (next.w-curr.w)*time + curr.w;
488
                }
489
                
490
              break;
491
      default:computeSegmentAndTime(time);
492

    
493
              if( mTmpVec>=0 && mTmpVec<numPoints )
494
                {
495
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
496
                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
497
                  {
498
                  int vecNext = getNext(mTmpVec,time);
499
                  next = vv.elementAt(vecNext);
500
                  tmpCache2 = vc.elementAt(vecNext);
501

    
502
                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z || tmpCache2.cached[3]!=next.w ) recomputeCache();
503
                  }
504

    
505
                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
506

    
507
                mSegment = mTmpSeg;
508
                time = mTmpTime-mTmpVec;
509
                tmpCache1 = vc.elementAt(mTmpVec);
510
                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
511

    
512
                if( vn!=null )
513
                  {
514
                  time = noise(time,mTmpVec);
515
              
516
                  computeOrthonormalBaseMore(time, tmpCache1);
517

    
518
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
519
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
520
                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
521
                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
522
                  }
523
                else
524
                  {
525
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
526
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
527
                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
528
                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
529
                  }
530

    
531
                break;
532
                }
533
      }
534
    }  
535

    
536
  }
537
///////////////////////////////////////////////////////////////////////////////////////////////////
538
//
(10-10/18)