Project

General

Profile

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

library / src / main / java / org / distorted / library / type / DynamicQuat.java @ 9dacabea

1 d333eb6b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4 46b572b5 Leszek Koltunski
// This file is part of Distorted.                                                               //
5 d333eb6b Leszek Koltunski
//                                                                                               //
6 46b572b5 Leszek Koltunski
// Distorted is free software: you can redistribute it and/or modify                             //
7 d333eb6b Leszek Koltunski
// 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 46b572b5 Leszek Koltunski
// Distorted is distributed in the hope that it will be useful,                                  //
12 d333eb6b Leszek Koltunski
// 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 46b572b5 Leszek Koltunski
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18 d333eb6b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
19
20 a4835695 Leszek Koltunski
package org.distorted.library.type;
21 6a06a912 Leszek Koltunski
22
import java.util.Vector;
23
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
/** 
26 568b29d8 Leszek Koltunski
* A 4-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static4Ds.
28 6a06a912 Leszek Koltunski
* 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 568b29d8 Leszek Koltunski
public class DynamicQuat extends Dynamic implements Data4D
33 6a06a912 Leszek Koltunski
  {
34
 
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36 649544b8 Leszek Koltunski
// Here we implement our own Cache as we need something slightly different.
37 6a06a912 Leszek Koltunski
// omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
38
//  
39
// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
40 cacc63de Leszek Koltunski
// still valid and if not - rebuild the Cache.
41 6a06a912 Leszek Koltunski
  
42 649544b8 Leszek Koltunski
  private class VectorCacheQuat
43 6a06a912 Leszek Koltunski
    {
44
    float omega, sinOmega,cosOmega;
45
    float vx,vy,vz,vw;
46
    }
47
  
48 649544b8 Leszek Koltunski
  private Vector<VectorCacheQuat> vc;
49
  private VectorCacheQuat tmp1, tmp2;
50 6a06a912 Leszek Koltunski
51 568b29d8 Leszek Koltunski
  private Vector<Static4D> vv;
52
  private Static4D curr, next;
53 6a06a912 Leszek Koltunski
 
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55 f871c455 Leszek Koltunski
// Abramowitz / Stegun
56 6a06a912 Leszek Koltunski
57
  private static float arcCos(float x)
58
    {
59
    if( x<0 )
60
      return 3.14159265358979f - (float)Math.sqrt(1+x)*(1.5707288f + 0.2121144f*x + 0.074261f*x*x + 0.0187293f*x*x*x);
61
     
62
    return (float)Math.sqrt(1-x)*(1.5707288f - 0.2121144f*x + 0.074261f*x*x - 0.0187293f*x*x*x);
63
    }
64
65
///////////////////////////////////////////////////////////////////////////////////////////////////
66 9dacabea Leszek Koltunski
67 6a06a912 Leszek Koltunski
  private void recomputeCache()
68
    {  
69
    if( numPoints>=2 )
70
      {
71
      int i, n;  
72 9dacabea Leszek Koltunski
      Static4D cu,ne;
73
      VectorCacheQuat vq;
74
75 6a06a912 Leszek Koltunski
      for(i=0; i<numPoints; i++)
76
        {
77
        n = i<numPoints-1 ? i+1:0;  
78
      
79 9dacabea Leszek Koltunski
        vq= vc.elementAt(i);
80
        cu= vv.elementAt(i);
81
        ne= vv.elementAt(n);
82 6a06a912 Leszek Koltunski
      
83 9dacabea Leszek Koltunski
        vq.vx = cu.x;
84
        vq.vy = cu.y;
85
        vq.vz = cu.z;
86
        vq.vw = cu.w;
87 6a06a912 Leszek Koltunski
    	
88 9dacabea Leszek Koltunski
        vq.cosOmega = cu.x*ne.x + cu.y*ne.y + cu.z*ne.z + cu.w*ne.w;
89 6a06a912 Leszek Koltunski
      	
90 9dacabea Leszek Koltunski
        if( vq.cosOmega<0 && n!=0 )  // do not invert the last quaternion even if we'd have to go the long way around!
91 6a06a912 Leszek Koltunski
          {
92 9dacabea Leszek Koltunski
          vq.cosOmega = -vq.cosOmega;
93
          ne.x = -ne.x;
94
          ne.y = -ne.y;
95
          ne.z = -ne.z;
96
          ne.w = -ne.w;
97 6a06a912 Leszek Koltunski
          }
98
      	
99 9dacabea Leszek Koltunski
        vq.sinOmega = (float)Math.sqrt(1-vq.cosOmega*vq.cosOmega);
100
        vq.omega = arcCos(vq.cosOmega);
101 6a06a912 Leszek Koltunski
        }
102
      }
103
   
104
    cacheDirty = false;
105
    }
106
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108
// PUBLIC API
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110
/**
111
 * Default constructor.
112
 */
113 568b29d8 Leszek Koltunski
  public DynamicQuat()
114 6a06a912 Leszek Koltunski
    {
115 3002bef3 Leszek Koltunski
    this(0,0.5f);
116 6a06a912 Leszek Koltunski
    }
117 8c893ffc Leszek Koltunski
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119
/**
120 c45c2ab1 Leszek Koltunski
 * Constructor setting the speed of interpolation and the number of revolutions.
121 8c893ffc Leszek Koltunski
 *
122 c45c2ab1 Leszek Koltunski
 * What constitutes 'one revolution' depends on the MODE:
123
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
124
 *
125
 * @param duration number of milliseconds it takes to do one revolution.
126
 * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
127 8c893ffc Leszek Koltunski
 */
128
  public DynamicQuat(int duration, float count)
129
    {
130 20dc919b Leszek Koltunski
    vv         = new Vector<>();
131
    vc         = new Vector<>();
132
    numPoints  = 0;
133 8c893ffc Leszek Koltunski
    cacheDirty = false;
134 20dc919b Leszek Koltunski
    mMode      = MODE_LOOP;
135
    mDuration  = duration;
136
    mCount     = count;
137 bdb341bc Leszek Koltunski
    mLastPos   = -1;
138 c45c2ab1 Leszek Koltunski
    mAccessType = ACCESS_TYPE_RANDOM;
139 3002bef3 Leszek Koltunski
    mDimension = 4;
140 8c893ffc Leszek Koltunski
    }
141
142 6a06a912 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
143
/**
144 568b29d8 Leszek Koltunski
 * Returns the location'th Static4D.
145 6a06a912 Leszek Koltunski
 *   
146
 * @param location the index of the Point we are interested in.
147 568b29d8 Leszek Koltunski
 * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
148 6a06a912 Leszek Koltunski
 */  
149 568b29d8 Leszek Koltunski
  public synchronized Static4D getPoint(int location)
150 6a06a912 Leszek Koltunski
    {
151
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
152
    }
153
  
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155
/**
156
 * Resets the location'th Point.
157
 * 
158
 * @param location the index of the Point we are setting.
159
 * @param x New value of its first float.
160
 */
161
  public synchronized void setPoint(int location, float x, float y, float z, float w)
162
    {
163
    if( location>=0 && location<numPoints )
164
      {
165
      curr = vv.elementAt(location);
166
   
167
      if( curr!=null )
168
        {
169
        curr.set(x,y,z,w);
170
        cacheDirty=true;
171
        }
172
      }
173
    }
174
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176
/**
177 568b29d8 Leszek Koltunski
 * Adds a new Static4D to the end of our list of Points to interpolate through.
178 6a06a912 Leszek Koltunski
 * <p>   
179
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
180 568b29d8 Leszek Koltunski
 * here, and later on {@link Static4D#set(float,float,float,float)} it to some new value and
181 6a06a912 Leszek Koltunski
 * the change will be seamlessly reflected in the interpolated path.  
182
 * <p>
183
 * A Point can be added multiple times.
184
 *   
185
 * @param v The Point to add.
186
 */    
187 568b29d8 Leszek Koltunski
  public synchronized void add(Static4D v)
188 6a06a912 Leszek Koltunski
    {
189
    if( v!=null )
190
      {
191
      vv.add(v);
192
      
193
      switch(numPoints)
194
         {
195
         case 0: 
196 649544b8 Leszek Koltunski
         case 1: vc.add(new VectorCacheQuat());
197
                 vc.add(new VectorCacheQuat());
198 6a06a912 Leszek Koltunski
        	     break;
199 649544b8 Leszek Koltunski
         default:vc.add(new VectorCacheQuat());
200 6a06a912 Leszek Koltunski
         }
201
202
       numPoints++;
203
       cacheDirty = true;
204
       }
205
    }
206
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208
/**
209 568b29d8 Leszek Koltunski
 * Adds a new Static4D to the location'th place in our List of Points to interpolate through.
210 6a06a912 Leszek Koltunski
 *   
211
 * @param location Index in our List to add the new Point at.
212 568b29d8 Leszek Koltunski
 * @param v The Static4D to add.
213 6a06a912 Leszek Koltunski
 */  
214 568b29d8 Leszek Koltunski
  public synchronized void add(int location, Static4D v)
215 6a06a912 Leszek Koltunski
    {
216
    if( v!=null )
217
      {
218
      vv.add(location, v);
219
      
220
      switch(numPoints)
221
        {
222
        case 0: 
223 649544b8 Leszek Koltunski
        case 1: vc.add(new VectorCacheQuat());
224
                vc.add(new VectorCacheQuat());
225 6a06a912 Leszek Koltunski
                break;
226 649544b8 Leszek Koltunski
        default:vc.add(location,new VectorCacheQuat());
227 6a06a912 Leszek Koltunski
        }
228
229
      numPoints++;
230
      cacheDirty = true;
231
      }
232
    }
233
  
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235
/**
236
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
237
 * 
238
 * @param v The Point to remove.
239
 * @return <code>true</code> if we have removed at least one Point.
240
 */
241 568b29d8 Leszek Koltunski
  public synchronized boolean remove(Static4D v)
242 6a06a912 Leszek Koltunski
    {
243
    int n = vv.indexOf(v);
244
    boolean found = false;
245
   
246
    while( n>=0 ) 
247
      {
248
      vv.remove(n);
249
     
250
      switch(numPoints)
251
        {
252
        case 0:
253
        case 1: break;
254
        case 2: vc.removeAllElements();
255
                break;
256
        default:vc.remove(n);
257
        }
258
259
      numPoints--;
260
      found = true;
261
      n = vv.indexOf(v);
262
      }
263
   
264
    if( found ) 
265
      {
266
      cacheDirty=true;
267
      }
268
   
269
    return found;
270
    }
271
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
/**
274
 * Removes a location'th Point from the List of Points we interpolate through.
275
 * 
276
 * @param location index of the Point we want to remove. 
277
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
278
 */
279
  public synchronized boolean remove(int location)
280
    {
281
    if( location>=0 && location<numPoints ) 
282
      {
283
      vv.removeElementAt(location);
284
      
285
      switch(numPoints)
286
        {
287
        case 0: 
288
        case 1: break;
289
        case 2: vc.removeAllElements();
290
                break;
291
        default:vc.removeElementAt(location);
292
        }
293
294
      numPoints--;
295
      cacheDirty = true; 
296
      return true;
297
      }
298
299
    return false;
300
    }
301
  
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303
/**
304
 * Removes all Points.
305
 */
306
  public synchronized void removeAll()
307
    {
308
    numPoints = 0;
309
    vv.removeAllElements();
310
    vc.removeAllElements();
311
    cacheDirty = false;
312
    }
313
  
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315
/**
316
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
317
 * Interpolation is done using the spherical linear algorithm, aka SLERP.
318
 * <p>
319 568b29d8 Leszek Koltunski
 * Since this is a 4-dimensional Dynamic, the resulting interpolated Static4D gets written
320 6a06a912 Leszek Koltunski
 * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
321
 * 
322 568b29d8 Leszek Koltunski
 * @param buffer Float buffer we will write the resulting Static4D to.
323 6a06a912 Leszek Koltunski
 * @param offset Offset in the buffer where to write the result.
324 c45c2ab1 Leszek Koltunski
 * @param time   Time of interpolation. Time=0.0 is the beginning of the first revolution, time=1.0 - the end
325
 *               of the first revolution, time=2.5 - the middle of the third revolution.
326
 *               What constitutes 'one revolution' depends on the MODE:
327
 *               {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
328
 **/
329 bdb341bc Leszek Koltunski
  synchronized void interpolate(float[] buffer, int offset, float time)
330 6a06a912 Leszek Koltunski
    {  
331
    switch(numPoints)
332
      {
333
      case 0: buffer[offset  ] = 0.0f;
334
              buffer[offset+1] = 0.0f;
335
              buffer[offset+2] = 0.0f;
336
              buffer[offset+3] = 0.0f;
337
              break;
338
      case 1: curr = vv.elementAt(0);
339
              buffer[offset  ] = curr.x; 
340
              buffer[offset+1] = curr.y;
341
              buffer[offset+2] = curr.z;
342
              buffer[offset+3] = curr.w;
343
              break;
344
      default:float t = time;
345 9dacabea Leszek Koltunski
              int vecCurr, segment;
346 6a06a912 Leszek Koltunski
              float scale0, scale1;
347 9dacabea Leszek Koltunski
348
              switch(mMode)
349
                {
350
                case MODE_LOOP: time = time*numPoints;
351
                                segment = (int)time;
352
                                vecCurr = segment;
353
                                break;
354
                case MODE_PATH: if( t>0.5f ) t = 1.0f-t;
355
                                time = 2*t*(numPoints-1);
356
                                segment = (int)(2*t*(numPoints-1));
357
                                vecCurr = segment;
358
                                break;
359
                case MODE_JUMP: time = time*(numPoints-1);
360
                                segment = (int)time;
361
                                vecCurr = segment;
362
                                break;
363
                default       : vecCurr = 0;
364
                                segment = 0;
365
                }
366
367 6a06a912 Leszek Koltunski
              if( vecCurr>=0 && vecCurr<numPoints )
368
                {
369 9dacabea Leszek Koltunski
                int vecNext = getNext(vecCurr,t);
370
371 6a06a912 Leszek Koltunski
                curr = vv.elementAt(vecCurr);
372
                tmp1 = vc.elementAt(vecCurr);
373 9dacabea Leszek Koltunski
                next = vv.elementAt(vecNext);
374
375
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
376
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
377
                  {
378
                  tmp2 = vc.elementAt(vecNext);
379
380
                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
381
                  }
382
383
                mSegment = segment;
384
385
                time = time-vecCurr;
386
387 6a06a912 Leszek Koltunski
                if( tmp1.sinOmega==0 )
388
                  {
389 9dacabea Leszek Koltunski
                  scale0 = 1f;
390
                  scale1 = 0f;
391 6a06a912 Leszek Koltunski
                  }
392 9dacabea Leszek Koltunski
                else if( tmp1.cosOmega < 0.99 )
393 6a06a912 Leszek Koltunski
                  {
394
                  scale0 = (float)Math.sin( (1f-time)*tmp1.omega ) / tmp1.sinOmega;
395
                  scale1 = (float)Math.sin(     time *tmp1.omega ) / tmp1.sinOmega;
396
                  }
397 9dacabea Leszek Koltunski
                else
398 6a06a912 Leszek Koltunski
                  {
399
                  scale0 = 1f-time;
400
                  scale1 = time;
401
                  }
402
403
                buffer[offset  ] = scale0*curr.x + scale1*next.x;
404 9dacabea Leszek Koltunski
                buffer[offset+1] = scale0*curr.y + scale1*next.y;
405
                buffer[offset+2] = scale0*curr.z + scale1*next.z;
406
                buffer[offset+3] = scale0*curr.w + scale1*next.w;
407
408 6a06a912 Leszek Koltunski
                break;
409
                }
410
      }
411
    }  
412
413
  }