Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
424
///////////////////////////////////////////////////////////////////////////////////////////////////
425

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

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

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

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

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

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

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

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

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

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

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