Project

General

Profile

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

library / src / main / java / org / distorted / library / InterpolatorQuat.java @ 6a06a912

1
package org.distorted.library;
2

    
3
import java.util.Vector;
4

    
5
///////////////////////////////////////////////////////////////////////////////////////////////////
6
/** 
7
* A 4-dimensional implementation of the Interpolator class to interpolate between a list 
8
* of Float4Ds.
9
* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
10
* SLERP. Noise not supported (yet?).
11
*/
12

    
13
public class InterpolatorQuat extends Interpolator 
14
  {
15
 
16
///////////////////////////////////////////////////////////////////////////////////////////////////
17
// omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
18
//  
19
// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
20
// still valid and if not - rebuild the Cache
21
  
22
  private class VectorCache
23
    {
24
    float omega, sinOmega,cosOmega;
25
    float vx,vy,vz,vw;
26
    }
27
  
28
  private Vector<VectorCache> vc;
29
  private VectorCache tmp1, tmp2;
30

    
31
  private Vector<Float4D> vv;
32
  private Float4D curr, next;
33
 
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35
//Abramowitz / Stegun
36

    
37
  private static float arcCos(float x)
38
    {
39
    if( x<0 )
40
      return 3.14159265358979f - (float)Math.sqrt(1+x)*(1.5707288f + 0.2121144f*x + 0.074261f*x*x + 0.0187293f*x*x*x);
41
     
42
    return (float)Math.sqrt(1-x)*(1.5707288f - 0.2121144f*x + 0.074261f*x*x - 0.0187293f*x*x*x);
43
    }
44

    
45
///////////////////////////////////////////////////////////////////////////////////////////////////
46
// Quaternion Interpolator doesn't support noise
47
  
48
  synchronized void createNoise()
49
    {
50

    
51
    }
52
  
53
///////////////////////////////////////////////////////////////////////////////////////////////////
54
  
55
  private void recomputeCache()
56
    {  
57
    if( numPoints>=2 )
58
      {
59
      int i, n;  
60
     
61
      for(i=0; i<numPoints; i++)
62
        {
63
        n = i<numPoints-1 ? i+1:0;  
64
      
65
        tmp1= vc.elementAt(i);
66
        tmp2= vc.elementAt(n);
67
        curr= vv.elementAt(i);
68
        next= vv.elementAt(n);
69
      
70
        tmp1.vx = curr.x;
71
        tmp1.vy = curr.y;
72
        tmp1.vz = curr.z;
73
        tmp1.vw = curr.w;
74
    	
75
        tmp1.cosOmega = curr.x*next.x + curr.y*next.y + curr.z*next.z + curr.w*next.w;
76
      	
77
        if( tmp1.cosOmega<0 && n!=0 )  // do not invert the last quaternion even if we'd have to go the long way around!
78
          {
79
          tmp1.cosOmega = -tmp1.cosOmega;
80
          next.x = -next.x;
81
          next.y = -next.y;
82
          next.z = -next.z;
83
          next.w = -next.w;
84
          }
85
      	
86
        tmp1.sinOmega = (float)Math.sqrt(1-tmp1.cosOmega*tmp1.cosOmega);
87
        tmp1.omega = arcCos(tmp1.cosOmega);
88
        }
89
      }
90
   
91
    cacheDirty = false;
92
    }
93

    
94
///////////////////////////////////////////////////////////////////////////////////////////////////
95
// PUBLIC API
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97
/**
98
 * Default constructor.
99
 */
100
  public InterpolatorQuat()
101
    {
102
    vv = new Vector<Float4D>();
103
    vc = new Vector<VectorCache>();
104
    numPoints = 0;
105
    cacheDirty = false;
106
    mMode = MODE_LOOP;
107
    mDuration = 0;
108
    mCount = 0.5f;
109
    mNoise = 0.0f;
110
    }
111
  
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113
/**
114
 * Returns the location'th Float4D. 
115
 *   
116
 * @param location the index of the Point we are interested in.
117
 * @return The Float4D, if 0<=location&lt;getNumPoints(), or null otherwise. 
118
 */  
119
  public synchronized Float4D getPoint(int location)
120
    {
121
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
122
    }
123
  
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125
/**
126
 * Resets the location'th Point.
127
 * 
128
 * @param location the index of the Point we are setting.
129
 * @param x New value of its first float.
130
 */
131
  public synchronized void setPoint(int location, float x, float y, float z, float w)
132
    {
133
    if( location>=0 && location<numPoints )
134
      {
135
      curr = vv.elementAt(location);
136
   
137
      if( curr!=null )
138
        {
139
        curr.set(x,y,z,w);
140
        cacheDirty=true;
141
        }
142
      }
143
    }
144

    
145
///////////////////////////////////////////////////////////////////////////////////////////////////
146
/**
147
 * Adds a new Float4D to the end of our list of Points to interpolate through.
148
 * <p>   
149
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
150
 * here, and later on {@link Float4D#set(float,float,float,float)} it to some new value and 
151
 * the change will be seamlessly reflected in the interpolated path.  
152
 * <p>
153
 * A Point can be added multiple times.
154
 *   
155
 * @param v The Point to add.
156
 */    
157
  public synchronized void add(Float4D v)
158
    {
159
    if( v!=null )
160
      {
161
      vv.add(v);
162
      
163
      switch(numPoints)
164
         {
165
         case 0: 
166
         case 1: vc.add(new VectorCache());
167
                 vc.add(new VectorCache());
168
        	     break;
169
         default:vc.add(new VectorCache());
170
         }
171

    
172
       numPoints++;
173
       cacheDirty = true;
174
       }
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
/**
179
 * Adds a new Float4D to the location'th place in our List of Points to interpolate through.  
180
 *   
181
 * @param location Index in our List to add the new Point at.
182
 * @param v The Float4D to add.
183
 */  
184
  public synchronized void add(int location, Float4D v)
185
    {
186
    if( v!=null )
187
      {
188
      vv.add(location, v);
189
      
190
      switch(numPoints)
191
        {
192
        case 0: 
193
        case 1: vc.add(new VectorCache());
194
                vc.add(new VectorCache());
195
                break;
196
        default:vc.add(location,new VectorCache());
197
        }
198

    
199
      numPoints++;
200
      cacheDirty = true;
201
      }
202
    }
203
  
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205
/**
206
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
207
 * 
208
 * @param v The Point to remove.
209
 * @return <code>true</code> if we have removed at least one Point.
210
 */
211
  public synchronized boolean remove(Float4D v)
212
    {
213
    int n = vv.indexOf(v);
214
    boolean found = false;
215
   
216
    while( n>=0 ) 
217
      {
218
      vv.remove(n);
219
     
220
      switch(numPoints)
221
        {
222
        case 0:
223
        case 1: break;
224
        case 2: vc.removeAllElements();
225
                break;
226
        default:vc.remove(n);
227
        }
228

    
229
      numPoints--;
230
      found = true;
231
      n = vv.indexOf(v);
232
      }
233
   
234
    if( found ) 
235
      {
236
      cacheDirty=true;
237
      }
238
   
239
    return found;
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243
/**
244
 * Removes a location'th Point from the List of Points we interpolate through.
245
 * 
246
 * @param location index of the Point we want to remove. 
247
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
248
 */
249
  public synchronized boolean remove(int location)
250
    {
251
    if( location>=0 && location<numPoints ) 
252
      {
253
      vv.removeElementAt(location);
254
      
255
      switch(numPoints)
256
        {
257
        case 0: 
258
        case 1: break;
259
        case 2: vc.removeAllElements();
260
                break;
261
        default:vc.removeElementAt(location);
262
        }
263

    
264
      numPoints--;
265
      cacheDirty = true; 
266
      return true;
267
      }
268

    
269
    return false;
270
    }
271
  
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
/**
274
 * Removes all Points.
275
 */
276
  public synchronized void removeAll()
277
    {
278
    numPoints = 0;
279
    vv.removeAllElements();
280
    vc.removeAllElements();
281
    cacheDirty = false;
282
    }
283
  
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
/**
286
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
287
 * Interpolation is done using the spherical linear algorithm, aka SLERP.
288
 * <p>
289
 * Since this is a 4-dimensional Interpolator, the resulting interpolated Float4D gets written
290
 * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
291
 * 
292
 * @param buffer Float buffer we will write the resulting Float4D to.
293
 * @param offset Offset in the buffer where to write the result.
294
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
295
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
296
 */    
297
  public synchronized void interpolate(float[] buffer, int offset, float time)
298
    {  
299
    switch(numPoints)
300
      {
301
      case 0: buffer[offset  ] = 0.0f;
302
              buffer[offset+1] = 0.0f;
303
              buffer[offset+2] = 0.0f;
304
              buffer[offset+3] = 0.0f;
305
              break;
306
      case 1: curr = vv.elementAt(0);
307
              buffer[offset  ] = curr.x; 
308
              buffer[offset+1] = curr.y;
309
              buffer[offset+2] = curr.z;
310
              buffer[offset+3] = curr.w;
311
              break;
312
      default:float t = time;
313
              float scale0, scale1;
314
  
315
              if( mMode==MODE_JUMP ) time = time*(numPoints-1);
316
              else if( mMode==MODE_PATH || numPoints==2 ) time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
317
              else time = time*numPoints;
318
              
319
              int vecNext, vecCurr = (int)time;
320
              time = time-vecCurr;
321
      
322
              if( vecCurr>=0 && vecCurr<numPoints )
323
                {
324
                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
325
                   
326
                switch(mMode)
327
                  {
328
                  case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
329
                                  break;
330
                  case MODE_PATH: if( t<0.5f ) vecNext = vecCurr+1;  
331
                                  else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
332
                                  break;
333
                  case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
334
                                  break;
335
                  default       : vecNext = 0;                
336
                  }
337
     
338
                curr = vv.elementAt(vecCurr);
339
                next = vv.elementAt(vecNext);
340
                tmp1 = vc.elementAt(vecCurr);
341
                tmp2 = vc.elementAt(vecNext);
342
              
343
                if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
344
               
345
                if( tmp1.sinOmega==0 )
346
                  {
347
                  scale0 = 0f;
348
                  scale1 = 1f;
349
                  }
350
                else if( tmp1.cosOmega < 0.99 ) 
351
                  {
352
                  scale0 = (float)Math.sin( (1f-time)*tmp1.omega ) / tmp1.sinOmega;
353
                  scale1 = (float)Math.sin(     time *tmp1.omega ) / tmp1.sinOmega;
354
                  }
355
                else 
356
                  {
357
                  scale0 = 1f-time;
358
                  scale1 = time;
359
                  }
360

    
361
                buffer[offset  ] = scale0*curr.x + scale1*next.x;
362
                buffer[offset+1] = scale0*curr.y + scale1*next.y; 
363
                buffer[offset+2] = scale0*curr.z + scale1*next.z; 
364
                buffer[offset+3] = scale0*curr.w + scale1*next.w; 
365
                
366
                break;
367
                }
368
      }
369
    }  
370

    
371
  }
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
//
(28-28/28)