Project

General

Profile

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

library / src / main / java / org / distorted / library / type / DynamicQuat.java @ 246d021c

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