Project

General

Profile

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

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

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