Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic3D.java @ 86b434a0

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 Vector<Static3D> vv;
33
  private Static3D prev, curr, next;
34
/*
35
private float a0,a1,a2;
36
private float f0, f1;
37
private float b00,b01,b02;
38
private float b10,b11,b12;
39
private float b20,b21,b22;
40
private float oldTime;
41
*/
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43
// no array bounds checking!
44
  
45
  private void vec(int c)
46
    {
47
    int p = c>0 ? c-1: numPoints-1;
48
    int n = c<numPoints-1 ? c+1: 0;
49
    
50
    prev = vv.elementAt(p);
51
    curr = vv.elementAt(c);
52
    next = vv.elementAt(n);
53

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

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

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
/**
185
 * Default constructor.
186
 *
187
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
188
 *                 last and back to the first
189
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
190
 *                 to the last, back to first, and to the last again.
191
 */
192
  public Dynamic3D(int duration, float count)
193
    {
194
    super(duration,count,3);
195
    vv = new Vector<>();
196
    }
197

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

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

    
263
      numPoints++;
264
      cacheDirty = true;
265
      }
266
    }
267

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

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

    
329
      numPoints--;
330
      found = true;
331
      n = vv.indexOf(v);
332
      }
333
   
334
    if( found ) 
335
      {
336
      cacheDirty=true;
337
      }
338
   
339
    return found;
340
    }
341

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

    
368
      numPoints--;
369
      cacheDirty = true; 
370
      return true;
371
      }
372

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

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

    
401
  public synchronized void setNoise(Static3D noise)
402
    {
403
    if( vn==null )
404
      {
405
      vn = new Vector<>();
406
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
407

    
408
      if( mDimension>=2 )
409
        {
410
        mFactor = new float[mDimension-1];
411
        }
412

    
413
      mNoise = new float[mDimension];
414
      }
415

    
416
    if( noise.x<0.0f ) noise.x = 0.0f;
417
    if( noise.x>1.0f ) noise.x = 1.0f;
418
    if( noise.y<0.0f ) noise.y = 0.0f;
419
    if( noise.y>1.0f ) noise.y = 1.0f;
420
    if( noise.z<0.0f ) noise.z = 0.0f;
421
    if( noise.z>1.0f ) noise.z = 1.0f;
422

    
423
    mNoise[0] = noise.x;
424
    mNoise[1] = noise.y;
425
    mNoise[2] = noise.z;
426
    }
427

    
428
///////////////////////////////////////////////////////////////////////////////////////////////////
429
/**
430
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
431
 * <p>
432
 * Since this is a 3-dimensional Dynamic, the resulting interpolated Static3D gets written
433
 * to three locations in the buffer: buffer[offset], buffer[offset+1] and buffer[offset+2]. 
434
 * 
435
 * @param buffer Float buffer we will write the resulting Static3D to.
436
 * @param offset Offset in the buffer where to write the result.
437
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
438
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
439
 */    
440
  synchronized void interpolate(float[] buffer, int offset, float time)
441
    {  
442
    switch(numPoints)
443
      {
444
      case 0: buffer[offset  ] = 0.0f;
445
              buffer[offset+1] = 0.0f;
446
              buffer[offset+2] = 0.0f;
447
              break;
448
      case 1: curr = vv.elementAt(0);
449
              buffer[offset  ] = curr.x;
450
              buffer[offset+1] = curr.y;
451
              buffer[offset+2] = curr.z;
452
              break;
453
      case 2: curr = vv.elementAt(0);
454
              next = vv.elementAt(1);
455

    
456
              int segment2= (int)(2*time);
457

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

    
460
              if( vn!=null )
461
                {
462
                if( segment2 != mSegment )
463
                  {
464
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
465
                  mSegment = segment2;
466
                  }
467

    
468
                time = noise(time,0);
469
            
470
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
471
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
472
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
473
                }
474
              else
475
                {
476
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
477
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
478
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
479
                }
480
             
481
              break;
482
      default:float t = time;
483
              int vecCurr, segment;
484

    
485
              switch(mMode)
486
                {
487
                case MODE_LOOP: time = time*numPoints;
488
                                segment = (int)time;
489
                                vecCurr = segment;
490
                                break;
491
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
492

    
493
                                if( t<0.5f )
494
                                  {
495
                                  time = 2*t*(numPoints-1);
496
                                  vecCurr = segment;
497
                                  }
498
                                else
499
                                  {
500
                                  time = 2*(1-t)*(numPoints-1);
501
                                  vecCurr = 2*numPoints-3-segment;
502
                                  }
503
                                break;
504
                case MODE_JUMP: time = time*(numPoints-1);
505
                                segment = (int)time;
506
                                vecCurr = segment;
507
                                break;
508
                default       : vecCurr = 0;
509
                                segment = 0;
510
                }
511

    
512
              if( vecCurr>=0 && vecCurr<numPoints )
513
                {
514
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
515
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
516
                  {
517
                  int vecNext;
518

    
519
                  switch(mMode)
520
                    {
521
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
522
                                    break;
523
                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
524
                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;
525
                                    break;
526
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
527
                                    break;
528
                    default       : vecNext = 0;
529
                    }
530

    
531
                  next = vv.elementAt(vecNext);
532
                  tmp2 = vc.elementAt(vecNext);
533

    
534
                  if( tmp2.cached[0]!=next.x ) recomputeCache();
535
                  }
536

    
537
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
538

    
539
                mSegment = segment;
540

    
541
                time = time-vecCurr;
542
            
543
                tmp1 = vc.elementAt(vecCurr);
544
               
545
                if( vn!=null )
546
                  {
547
                  time = noise(time,vecCurr);
548
              
549
                  computeOrthonormalBaseMore(time,tmp1);
550
                 
551
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
552
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
553
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
554
/*
555
                  float d0 = buffer[offset+0] - a0;
556
                  float d1 = buffer[offset+1] - a1;
557
                  float d2 = buffer[offset+2] - a2;
558

    
559
                  float distSQ = d0*d0+d1*d1+d2*d2;
560

    
561
                  if( distSQ>500.0f )
562
                    {
563
                    android.util.Log.e("dyn3D", "distSQ="+distSQ);
564
                    android.util.Log.e("dyn3D", "factors old0="+f0+" new0="+mFactor[0]+" old1="+f1+" new1="+mFactor[1]);
565
                    android.util.Log.e("dyn3D", "first  base: old ("+b00+","+b01+","+b02+") new ("+baseV[0][0]+","+baseV[0][1]+","+baseV[0][2]+")");
566
                    android.util.Log.e("dyn3D", "second base: old ("+b10+","+b11+","+b12+") new ("+baseV[1][0]+","+baseV[1][1]+","+baseV[1][2]+")");
567
                    android.util.Log.e("dyn3D", "third  base: old ("+b20+","+b21+","+b22+") new ("+baseV[2][0]+","+baseV[2][1]+","+baseV[2][2]+")");
568

    
569
                    String s1= "velocity: old (";
570
                    String s2= "acceleration: old (";
571

    
572
                    for(int i=0; i<mDimension; i++)
573
                      {
574
                      s1 += (((3*tmp1.a[i]*oldTime+2*tmp1.b[i])*oldTime+tmp1.c[i])+(i==mDimension-1 ? ") new (":","));
575
                      s2 += ( (6*tmp1.a[i]*oldTime+2*tmp1.b[i])                   +(i==mDimension-1 ? ") new (":","));
576
                      }
577

    
578
                    for(int i=0; i<mDimension; i++)
579
                      {
580
                      s1 += (((3*tmp1.a[i]*time+2*tmp1.b[i])*time+tmp1.c[i])+(i==mDimension-1 ? ")":","));
581
                      s2 += ( (6*tmp1.a[i]*time+2*tmp1.b[i])                +(i==mDimension-1 ? ")":","));
582
                      }
583

    
584
                    android.util.Log.e("dyn3D", s1);
585
                    android.util.Log.e("dyn3D", s2);
586

    
587
                    computeOrthonormalBaseMoreDebug(oldTime,tmp1);
588
                    computeOrthonormalBaseMoreDebug(   time,tmp1);
589
                    }
590

    
591
                  a0 = buffer[offset+0];
592
                  a1 = buffer[offset+1];
593
                  a2 = buffer[offset+2];
594

    
595
                  f0 = mFactor[0];
596
                  f1 = mFactor[1];
597

    
598
                  b00 = baseV[0][0];
599
                  b01 = baseV[0][1];
600
                  b02 = baseV[0][2];
601

    
602
                  b10 = baseV[1][0];
603
                  b11 = baseV[1][1];
604
                  b12 = baseV[1][2];
605

    
606
                  b20 = baseV[2][0];
607
                  b21 = baseV[2][1];
608
                  b22 = baseV[2][2];
609

    
610
                  oldTime = time;
611
*/
612
                  }
613
                else
614
                  {
615
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
616
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
617
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
618
                  }
619
               
620
                break;
621
                }
622
       }
623
     }  
624

    
625
  }
626
///////////////////////////////////////////////////////////////////////////////////////////////////
627
//
(9-9/17)