Project

General

Profile

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

library / src / main / java / org / distorted / library / type / DynamicQuat.java @ 8c893ffc

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
* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
29
* SLERP. Noise not supported (yet?).
30
*/
31

    
32
public class DynamicQuat extends Dynamic implements Data4D
33
  {
34
 
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36
// omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
37
//  
38
// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
39
// still valid and if not - rebuild the Cache
40
  
41
  private class VectorCache
42
    {
43
    float omega, sinOmega,cosOmega;
44
    float vx,vy,vz,vw;
45
    }
46
  
47
  private Vector<VectorCache> vc;
48
  private VectorCache tmp1, tmp2;
49

    
50
  private Vector<Static4D> vv;
51
  private Static4D curr, next;
52
 
53
///////////////////////////////////////////////////////////////////////////////////////////////////
54
//Abramowitz / Stegun
55

    
56
  private static float arcCos(float x)
57
    {
58
    if( x<0 )
59
      return 3.14159265358979f - (float)Math.sqrt(1+x)*(1.5707288f + 0.2121144f*x + 0.074261f*x*x + 0.0187293f*x*x*x);
60
     
61
    return (float)Math.sqrt(1-x)*(1.5707288f - 0.2121144f*x + 0.074261f*x*x - 0.0187293f*x*x*x);
62
    }
63

    
64
///////////////////////////////////////////////////////////////////////////////////////////////////
65
// Quaternion Dynamic doesn't support noise
66
  
67
  synchronized void createNoise()
68
    {
69

    
70
    }
71
  
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73
  
74
  private void recomputeCache()
75
    {  
76
    if( numPoints>=2 )
77
      {
78
      int i, n;  
79
     
80
      for(i=0; i<numPoints; i++)
81
        {
82
        n = i<numPoints-1 ? i+1:0;  
83
      
84
        tmp1= vc.elementAt(i);
85
        tmp2= vc.elementAt(n);
86
        curr= vv.elementAt(i);
87
        next= vv.elementAt(n);
88
      
89
        tmp1.vx = curr.x;
90
        tmp1.vy = curr.y;
91
        tmp1.vz = curr.z;
92
        tmp1.vw = curr.w;
93
    	
94
        tmp1.cosOmega = curr.x*next.x + curr.y*next.y + curr.z*next.z + curr.w*next.w;
95
      	
96
        if( tmp1.cosOmega<0 && n!=0 )  // do not invert the last quaternion even if we'd have to go the long way around!
97
          {
98
          tmp1.cosOmega = -tmp1.cosOmega;
99
          next.x = -next.x;
100
          next.y = -next.y;
101
          next.z = -next.z;
102
          next.w = -next.w;
103
          }
104
      	
105
        tmp1.sinOmega = (float)Math.sqrt(1-tmp1.cosOmega*tmp1.cosOmega);
106
        tmp1.omega = arcCos(tmp1.cosOmega);
107
        }
108
      }
109
   
110
    cacheDirty = false;
111
    }
112

    
113
///////////////////////////////////////////////////////////////////////////////////////////////////
114
// PUBLIC API
115
///////////////////////////////////////////////////////////////////////////////////////////////////
116
/**
117
 * Default constructor.
118
 */
119
  public DynamicQuat()
120
    {
121
    vv = new Vector<>();
122
    vc = new Vector<>();
123
    numPoints = 0;
124
    cacheDirty = false;
125
    mMode = MODE_LOOP;
126
    mDuration = 0;
127
    mCount = 0.5f;
128
    mNoise = 0.0f;
129
    }
130

    
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132

    
133
/**
134
 * Default constructor.
135
 *
136
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
137
 *                 last and back to the first
138
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
139
 *                 to the last, back to first, and to the last again.
140
 */
141
  public DynamicQuat(int duration, float count)
142
    {
143
    vv = new Vector<>();
144
    vc = new Vector<>();
145
    numPoints = 0;
146
    cacheDirty = false;
147
    mMode = MODE_LOOP;
148
    mDuration = duration;
149
    mCount = count;
150
    mNoise = 0.0f;
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154
/**
155
 * Returns the location'th Static4D.
156
 *   
157
 * @param location the index of the Point we are interested in.
158
 * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
159
 */  
160
  public synchronized Static4D getPoint(int location)
161
    {
162
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
163
    }
164
  
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
/**
167
 * Resets the location'th Point.
168
 * 
169
 * @param location the index of the Point we are setting.
170
 * @param x New value of its first float.
171
 */
172
  public synchronized void setPoint(int location, float x, float y, float z, float w)
173
    {
174
    if( location>=0 && location<numPoints )
175
      {
176
      curr = vv.elementAt(location);
177
   
178
      if( curr!=null )
179
        {
180
        curr.set(x,y,z,w);
181
        cacheDirty=true;
182
        }
183
      }
184
    }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187
/**
188
 * Adds a new Static4D to the end of our list of Points to interpolate through.
189
 * <p>   
190
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
191
 * here, and later on {@link Static4D#set(float,float,float,float)} it to some new value and
192
 * the change will be seamlessly reflected in the interpolated path.  
193
 * <p>
194
 * A Point can be added multiple times.
195
 *   
196
 * @param v The Point to add.
197
 */    
198
  public synchronized void add(Static4D v)
199
    {
200
    if( v!=null )
201
      {
202
      vv.add(v);
203
      
204
      switch(numPoints)
205
         {
206
         case 0: 
207
         case 1: vc.add(new VectorCache());
208
                 vc.add(new VectorCache());
209
        	     break;
210
         default:vc.add(new VectorCache());
211
         }
212

    
213
       numPoints++;
214
       cacheDirty = true;
215
       }
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219
/**
220
 * Adds a new Static4D to the location'th place in our List of Points to interpolate through.
221
 *   
222
 * @param location Index in our List to add the new Point at.
223
 * @param v The Static4D to add.
224
 */  
225
  public synchronized void add(int location, Static4D v)
226
    {
227
    if( v!=null )
228
      {
229
      vv.add(location, v);
230
      
231
      switch(numPoints)
232
        {
233
        case 0: 
234
        case 1: vc.add(new VectorCache());
235
                vc.add(new VectorCache());
236
                break;
237
        default:vc.add(location,new VectorCache());
238
        }
239

    
240
      numPoints++;
241
      cacheDirty = true;
242
      }
243
    }
244
  
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246
/**
247
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
248
 * 
249
 * @param v The Point to remove.
250
 * @return <code>true</code> if we have removed at least one Point.
251
 */
252
  public synchronized boolean remove(Static4D v)
253
    {
254
    int n = vv.indexOf(v);
255
    boolean found = false;
256
   
257
    while( n>=0 ) 
258
      {
259
      vv.remove(n);
260
     
261
      switch(numPoints)
262
        {
263
        case 0:
264
        case 1: break;
265
        case 2: vc.removeAllElements();
266
                break;
267
        default:vc.remove(n);
268
        }
269

    
270
      numPoints--;
271
      found = true;
272
      n = vv.indexOf(v);
273
      }
274
   
275
    if( found ) 
276
      {
277
      cacheDirty=true;
278
      }
279
   
280
    return found;
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284
/**
285
 * Removes a location'th Point from the List of Points we interpolate through.
286
 * 
287
 * @param location index of the Point we want to remove. 
288
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
289
 */
290
  public synchronized boolean remove(int location)
291
    {
292
    if( location>=0 && location<numPoints ) 
293
      {
294
      vv.removeElementAt(location);
295
      
296
      switch(numPoints)
297
        {
298
        case 0: 
299
        case 1: break;
300
        case 2: vc.removeAllElements();
301
                break;
302
        default:vc.removeElementAt(location);
303
        }
304

    
305
      numPoints--;
306
      cacheDirty = true; 
307
      return true;
308
      }
309

    
310
    return false;
311
    }
312
  
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314
/**
315
 * Removes all Points.
316
 */
317
  public synchronized void removeAll()
318
    {
319
    numPoints = 0;
320
    vv.removeAllElements();
321
    vc.removeAllElements();
322
    cacheDirty = false;
323
    }
324
  
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326
/**
327
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
328
 * Interpolation is done using the spherical linear algorithm, aka SLERP.
329
 * <p>
330
 * Since this is a 4-dimensional Dynamic, the resulting interpolated Static4D gets written
331
 * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
332
 * 
333
 * @param buffer Float buffer we will write the resulting Static4D to.
334
 * @param offset Offset in the buffer where to write the result.
335
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
336
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
337
 */    
338
  public synchronized void interpolate(float[] buffer, int offset, float time)
339
    {  
340
    switch(numPoints)
341
      {
342
      case 0: buffer[offset  ] = 0.0f;
343
              buffer[offset+1] = 0.0f;
344
              buffer[offset+2] = 0.0f;
345
              buffer[offset+3] = 0.0f;
346
              break;
347
      case 1: curr = vv.elementAt(0);
348
              buffer[offset  ] = curr.x; 
349
              buffer[offset+1] = curr.y;
350
              buffer[offset+2] = curr.z;
351
              buffer[offset+3] = curr.w;
352
              break;
353
      default:float t = time;
354
              float scale0, scale1;
355
  
356
              if( mMode==MODE_JUMP ) time = time*(numPoints-1);
357
              else if( mMode==MODE_PATH || numPoints==2 ) time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
358
              else time = time*numPoints;
359
              
360
              int vecNext, vecCurr = (int)time;
361
              time = time-vecCurr;
362
      
363
              if( vecCurr>=0 && vecCurr<numPoints )
364
                {
365
                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
366
                   
367
                switch(mMode)
368
                  {
369
                  case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
370
                                  break;
371
                  case MODE_PATH: if( t<0.5f ) vecNext = vecCurr+1;  
372
                                  else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
373
                                  break;
374
                  case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
375
                                  break;
376
                  default       : vecNext = 0;                
377
                  }
378
     
379
                curr = vv.elementAt(vecCurr);
380
                next = vv.elementAt(vecNext);
381
                tmp1 = vc.elementAt(vecCurr);
382
                tmp2 = vc.elementAt(vecNext);
383
              
384
                if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
385
               
386
                if( tmp1.sinOmega==0 )
387
                  {
388
                  scale0 = 0f;
389
                  scale1 = 1f;
390
                  }
391
                else if( tmp1.cosOmega < 0.99 ) 
392
                  {
393
                  scale0 = (float)Math.sin( (1f-time)*tmp1.omega ) / tmp1.sinOmega;
394
                  scale1 = (float)Math.sin(     time *tmp1.omega ) / tmp1.sinOmega;
395
                  }
396
                else 
397
                  {
398
                  scale0 = 1f-time;
399
                  scale1 = time;
400
                  }
401

    
402
                buffer[offset  ] = scale0*curr.x + scale1*next.x;
403
                buffer[offset+1] = scale0*curr.y + scale1*next.y; 
404
                buffer[offset+2] = scale0*curr.z + scale1*next.z; 
405
                buffer[offset+3] = scale0*curr.w + scale1*next.w; 
406
                
407
                break;
408
                }
409
      }
410
    }  
411

    
412
  }
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414
//
(10-10/14)