Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic3D.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 3-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static3Ds.
28
*/
29

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

    
158
        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
159
        }
160
      }
161
   
162
    cacheDirty = false;
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
// PUBLIC API
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168
/**
169
 * Default constructor.
170
 */
171
  public Dynamic3D()
172
    {
173
    super(0,0.5f,3);
174
    vv = new Vector<>();
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
/**
179
 * Constructor setting the speed of interpolation and the number of revolutions.
180
 *
181
 * What constitutes 'one revolution' depends on the MODE:
182
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
183
 *
184
 * @param duration number of milliseconds it takes to do one revolution.
185
 * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
186
 */
187
  public Dynamic3D(int duration, float count)
188
    {
189
    super(duration,count,3);
190
    vv = new Vector<>();
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194
/**
195
 * Returns the location'th Static3D.
196
 *   
197
 * @param location the index of the Point we are interested in.
198
 * @return The Static3D, if 0<=location&lt;getNumPoints(), or null otherwise.
199
 */  
200
  public synchronized Static3D getPoint(int location)
201
    {
202
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
203
    }
204
  
205
///////////////////////////////////////////////////////////////////////////////////////////////////
206
/**
207
 * Resets the location'th Point.
208
 * 
209
 * @param location the index of the Point we are setting.
210
 * @param x New value of its first float.
211
 */
212
  public synchronized void setPoint(int location, float x, float y, float z)
213
    {
214
    if( location>=0 && location<numPoints )
215
      {
216
      curr = vv.elementAt(location);
217
   
218
      if( curr!=null )
219
        {
220
        curr.set(x,y,z);
221
        cacheDirty=true;
222
        }
223
      }
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227
/**
228
 * Adds a new Static3D to the end of our list of Points to interpolate through.
229
 * <p>   
230
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
231
 * here, and later on {@link Static3D#set(float,float,float)} it to some new value and the
232
 * change will be seamlessly reflected in the interpolated path.  
233
 * <p>
234
 * A Point can be added multiple times.
235
 *   
236
 * @param v The Point to add.
237
 */    
238
  public synchronized void add(Static3D v)
239
    {
240
    if( v!=null )
241
      {
242
      vv.add(v);
243
        
244
      if( vn!=null ) vn.add(new VectorNoise());
245
       
246
      switch(numPoints)
247
        {
248
        case 0: break;
249
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
250
                break;
251
        case 2: vc.add(new VectorCache());
252
                vc.add(new VectorCache());
253
                vc.add(new VectorCache());
254
                break;
255
        default:vc.add(new VectorCache());
256
        }
257

    
258
      numPoints++;
259
      cacheDirty = true;
260
      }
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264
/**
265
 * Adds a new Static3D to the location'th place in our List of Points to interpolate through.
266
 *   
267
 * @param location Index in our List to add the new Point at.
268
 * @param v The Point to add.
269
 */  
270
  public synchronized void add(int location, Static3D v)
271
    {
272
    if( v!=null )
273
      {
274
      vv.add(location, v);
275
      
276
      if( vn!=null ) vn.add(new VectorNoise());
277
      
278
      switch(numPoints)
279
        {
280
        case 0: break;
281
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
282
                break;
283
        case 2: vc.add(new VectorCache());
284
                vc.add(new VectorCache());
285
                vc.add(new VectorCache());
286
                break;
287
        default:vc.add(location,new VectorCache());
288
        }
289

    
290
      numPoints++;
291
      cacheDirty = true;
292
      }
293
    }
294
  
295
///////////////////////////////////////////////////////////////////////////////////////////////////
296
/**
297
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
298
 * 
299
 * @param v The Point to remove.
300
 * @return <code>true</code> if we have removed at least one Point.
301
 */
302
  public synchronized boolean remove(Static3D v)
303
    {
304
    int n = vv.indexOf(v);
305
    boolean found = false;
306
   
307
    while( n>=0 ) 
308
      {
309
      vv.remove(n);
310
     
311
      if( vn!=null ) vn.remove(0);
312
     
313
      switch(numPoints)
314
        {
315
        case 0:
316
        case 1: 
317
        case 2: break;
318
        case 3: vc.removeAllElements();
319
                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
320
                break;
321
        default:vc.remove(n);
322
        }
323

    
324
      numPoints--;
325
      found = true;
326
      n = vv.indexOf(v);
327
      }
328
   
329
    if( found ) 
330
      {
331
      cacheDirty=true;
332
      }
333
   
334
    return found;
335
    }
336

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

    
363
      numPoints--;
364
      cacheDirty = true; 
365
      return true;
366
      }
367

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

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
/**
387
 * Sets the 'smoothness' of interpolation.
388
 * <p>
389
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
390
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
391
 * up and slowing down, etc.
392
 *
393
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
394
 */
395

    
396
  public synchronized void setNoise(Static3D noise)
397
    {
398
    if( vn==null )
399
      {
400
      vn = new Vector<>();
401
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
402

    
403
      if( mDimension>=2 )
404
        {
405
        mFactor = new float[mDimension-1];
406
        }
407

    
408
      mNoise = new float[mDimension];
409
      }
410

    
411
    if( noise.x<0.0f ) noise.x = 0.0f;
412
    if( noise.x>1.0f ) noise.x = 1.0f;
413
    if( noise.y<0.0f ) noise.y = 0.0f;
414
    if( noise.y>1.0f ) noise.y = 1.0f;
415
    if( noise.z<0.0f ) noise.z = 0.0f;
416
    if( noise.z>1.0f ) noise.z = 1.0f;
417

    
418
    mNoise[0] = noise.x;
419
    mNoise[1] = noise.y;
420
    mNoise[2] = noise.z;
421
    }
422

    
423
///////////////////////////////////////////////////////////////////////////////////////////////////
424

    
425
  synchronized void interpolate(float[] buffer, int offset, float time)
426
    {  
427
    switch(numPoints)
428
      {
429
      case 0: buffer[offset  ] = 0.0f;
430
              buffer[offset+1] = 0.0f;
431
              buffer[offset+2] = 0.0f;
432
              break;
433
      case 1: curr = vv.elementAt(0);
434
              buffer[offset  ] = curr.x;
435
              buffer[offset+1] = curr.y;
436
              buffer[offset+2] = curr.z;
437
              break;
438
      case 2: curr = vv.elementAt(0);
439
              next = vv.elementAt(1);
440

    
441
              int segment= (int)(2*time);
442

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

    
445
              if( vn!=null )
446
                {
447
                if( segment != mSegment )
448
                  {
449
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
450
                  mSegment = segment;
451
                  }
452

    
453
                time = noise(time,0);
454
            
455
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
456
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
457
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
458
                }
459
              else
460
                {
461
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
462
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
463
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
464
                }
465
             
466
              break;
467
      default:computeSegmentAndTime(time);
468

    
469
              if( mTmpVec>=0 && mTmpVec<numPoints )
470
                {
471
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
472
                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
473
                  {
474
                  int vecNext = getNext(mTmpVec,time);
475
                  next = vv.elementAt(vecNext);
476
                  tmpCache2 = vc.elementAt(vecNext);
477

    
478
                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z ) recomputeCache();
479
                  }
480

    
481
                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
482

    
483
                mSegment = mTmpSeg;
484
                time = mTmpTime-mTmpVec;
485
                tmpCache1 = vc.elementAt(mTmpVec);
486
                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
487

    
488
                if( vn!=null )
489
                  {
490
                  time = noise(time,mTmpVec);
491
              
492
                  computeOrthonormalBaseMore(time, tmpCache1);
493
                 
494
                  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]);
495
                  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]);
496
                  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]);
497
                  }
498
                else
499
                  {
500
                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
501
                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
502
                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
503
                  }
504
               
505
                break;
506
                }
507
       }
508
     }  
509

    
510
  }
511
///////////////////////////////////////////////////////////////////////////////////////////////////
512
//
(9-9/18)