Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic4D.java @ c0e4d5cf

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 4-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static4Ds.
28
*/
29

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
442
///////////////////////////////////////////////////////////////////////////////////////////////////
443

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

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

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

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

    
474
                time = noise(time,0);
475

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

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

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

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

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

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

    
517
                  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]);
518
                  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]);
519
                  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]);
520
                  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]);
521
                  }
522
                else
523
                  {
524
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
525
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
526
                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
527
                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
528
                  }
529

    
530
                break;
531
                }
532
      }
533
    }  
534

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