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.Random;
|
23
|
import java.util.Vector;
|
24
|
|
25
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
26
|
/** A class to interpolate between a List of Static{1,2,3,4}Ds.
|
27
|
* <p><ul>
|
28
|
* <li>if there is only one Point, just jump to it.
|
29
|
* <li>if there are two Points, linearly bounce between them
|
30
|
* <li>if there are more, interpolate a loop (or a path!) between them.
|
31
|
* </ul>
|
32
|
*/
|
33
|
|
34
|
// The way Interpolation between more than 2 Points is done:
|
35
|
//
|
36
|
// Def: let w[i] = (w[i](x), w[i](y), w[i](z)) be the direction and speed we have to be flying at Point P[i]
|
37
|
//
|
38
|
// time it takes to fly though one segment v[i] --> v[i+1] : 0.0 --> 1.0
|
39
|
// w[i] should be parallel to v[i+1] - v[i-1] (cyclic notation)
|
40
|
// |w[i]| proportional to | P[i]-P[i+1] |
|
41
|
//
|
42
|
// Given that the flight route (X(t), Y(t), Z(t)) from P(i) to P(i+1) (0<=t<=1) has to satisfy
|
43
|
// X(0) = P[i ](x), Y(0)=P[i ](y), Z(0)=P[i ](z), X'(0) = w[i ](x), Y'(0) = w[i ](y), Z'(0) = w[i ](z)
|
44
|
// X(1) = P[i+1](x), Y(1)=P[i+1](y), Z(1)=P[i+1](z), X'(1) = w[i+1](x), Y'(1) = w[i+1](y), Z'(1) = w[i+1](z)
|
45
|
//
|
46
|
// we have the solution: X(t) = at^3 + bt^2 + ct + d where
|
47
|
// a = 2*P[i](x) + w[i](x) - 2*P[i+1](x) + w[i+1](x)
|
48
|
// b = -3*P[i](x) - 2*w[i](x) + 3*P[i+1](x) - w[i+1](x)
|
49
|
// c = w[i](x)<br>
|
50
|
// d = P[i](x)
|
51
|
//
|
52
|
// and similarly Y(t) and Z(t).
|
53
|
|
54
|
public abstract class Dynamic
|
55
|
{
|
56
|
/**
|
57
|
* One revolution takes us from the first vector to the last and back to first through the shortest path.
|
58
|
*/
|
59
|
public static final int MODE_LOOP = 0;
|
60
|
/**
|
61
|
* We come back from the last to the first vector through the same way we got there.
|
62
|
*/
|
63
|
public static final int MODE_PATH = 1;
|
64
|
/**
|
65
|
* We just jump back from the last point to the first.
|
66
|
*/
|
67
|
public static final int MODE_JUMP = 2;
|
68
|
|
69
|
protected static Random mRnd = new Random();
|
70
|
|
71
|
protected static final int NUM_NOISE = 5; // used iff mNoise>0.0. Number of intermediary points between each pair of adjacent vectors
|
72
|
// where we randomize noise factors to make the way between the two vectors not so smooth.
|
73
|
|
74
|
protected int mDimension;
|
75
|
protected int numPoints;
|
76
|
protected int mVecCurr;
|
77
|
protected boolean cacheDirty; // VectorCache not up to date
|
78
|
protected int mMode; // LOOP, PATH or JUMP
|
79
|
protected long mDuration; // number of milliseconds it takes to do a full loop/path from first vector to the last and back to the first
|
80
|
protected float mCount; // number of loops/paths we will do; mCount = 1.5 means we go from the first vector to the last, back to first, and to the last again.
|
81
|
protected float mNoise; // how 'smooth' our path form each vector to the next is. mNoise = 0.0 (min) --> completely smooth; mNoise==1.0 (max) --> very uneven
|
82
|
|
83
|
protected class VectorNoise
|
84
|
{
|
85
|
float[][] n;
|
86
|
|
87
|
VectorNoise(int dim)
|
88
|
{
|
89
|
n = new float[dim][NUM_NOISE];
|
90
|
|
91
|
n[0][0] = mRnd.nextFloat();
|
92
|
for(int i=1; i<NUM_NOISE; i++) n[0][i] = n[0][i-1]+mRnd.nextFloat();
|
93
|
float sum = n[0][NUM_NOISE-1] + mRnd.nextFloat();
|
94
|
for(int i=0; i<NUM_NOISE; i++) n[0][i] /=sum;
|
95
|
|
96
|
for(int j=1; j<dim; j++)
|
97
|
{
|
98
|
for(int i=0; i<NUM_NOISE; i++) n[j][i] = mRnd.nextFloat()-0.5f;
|
99
|
}
|
100
|
}
|
101
|
}
|
102
|
|
103
|
protected Vector<VectorNoise> vn;
|
104
|
protected float[] mFactor;
|
105
|
|
106
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
107
|
// hide this from Javadoc
|
108
|
|
109
|
Dynamic()
|
110
|
{
|
111
|
}
|
112
|
|
113
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
114
|
|
115
|
public void interpolateMain(float[] buffer, int offset, long currentDuration)
|
116
|
{
|
117
|
if( mDuration<=0.0f )
|
118
|
{
|
119
|
interpolate(buffer,offset,mCount-(int)mCount);
|
120
|
}
|
121
|
else
|
122
|
{
|
123
|
float x = (float)currentDuration/mDuration;
|
124
|
|
125
|
if( x<=mCount || mCount<=0.0f )
|
126
|
{
|
127
|
interpolate(buffer,offset,x-(int)x);
|
128
|
}
|
129
|
}
|
130
|
}
|
131
|
|
132
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
133
|
|
134
|
public boolean interpolateMain(float[] buffer, int offset, long currentDuration, long step)
|
135
|
{
|
136
|
if( mDuration<=0.0f )
|
137
|
{
|
138
|
interpolate(buffer,offset,mCount-(int)mCount);
|
139
|
return false;
|
140
|
}
|
141
|
|
142
|
float x = (float)currentDuration/mDuration;
|
143
|
|
144
|
if( x<=mCount || mCount<=0.0f )
|
145
|
{
|
146
|
interpolate(buffer,offset,x-(int)x);
|
147
|
|
148
|
if( currentDuration+step > mDuration*mCount && mCount>0.0f )
|
149
|
{
|
150
|
interpolate(buffer,offset,mCount-(int)mCount);
|
151
|
return true;
|
152
|
}
|
153
|
}
|
154
|
|
155
|
return false;
|
156
|
}
|
157
|
|
158
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
159
|
|
160
|
protected float noise(float time,int vecNum)
|
161
|
{
|
162
|
float lower, upper, len;
|
163
|
float d = time*(NUM_NOISE+1);
|
164
|
int index = (int)d;
|
165
|
if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
|
166
|
VectorNoise tmpN = vn.elementAt(vecNum);
|
167
|
|
168
|
float t = d-index;
|
169
|
t = t*t*(3-2*t);
|
170
|
|
171
|
switch(index)
|
172
|
{
|
173
|
case 0 : for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise*tmpN.n[i+1][0]*t;
|
174
|
return time + mNoise*(d*tmpN.n[0][0]-time);
|
175
|
case NUM_NOISE: for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise*tmpN.n[i+1][NUM_NOISE-1]*(1-t);
|
176
|
len = ((float)NUM_NOISE)/(NUM_NOISE+1);
|
177
|
lower = len + mNoise*(tmpN.n[0][NUM_NOISE-1]-len);
|
178
|
return (1.0f-lower)*(d-NUM_NOISE) + lower;
|
179
|
default : float ya,yb;
|
180
|
|
181
|
for(int i=0;i<mDimension-1;i++)
|
182
|
{
|
183
|
yb = tmpN.n[i+1][index ];
|
184
|
ya = tmpN.n[i+1][index-1];
|
185
|
mFactor[i] = mNoise*((yb-ya)*t+ya);
|
186
|
}
|
187
|
|
188
|
len = ((float)index)/(NUM_NOISE+1);
|
189
|
lower = len + mNoise*(tmpN.n[0][index-1]-len);
|
190
|
len = ((float)index+1)/(NUM_NOISE+1);
|
191
|
upper = len + mNoise*(tmpN.n[0][index ]-len);
|
192
|
|
193
|
return (upper-lower)*(d-index) + lower;
|
194
|
}
|
195
|
}
|
196
|
|
197
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
198
|
// internal debugging only!
|
199
|
|
200
|
public String print()
|
201
|
{
|
202
|
return "duration="+mDuration+" count="+mCount+" Noise="+mNoise+" numVectors="+numPoints+" mMode="+mMode;
|
203
|
}
|
204
|
|
205
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
206
|
|
207
|
abstract void interpolate(float[] buffer, int offset, float time);
|
208
|
|
209
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
210
|
// PUBLIC API
|
211
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
212
|
|
213
|
/**
|
214
|
* Sets the mode of the interpolation to Loop, Path or Jump.
|
215
|
* <ul>
|
216
|
* <li>Loop is when we go from the first point all the way to the last, and the back to the first through
|
217
|
* the shortest way.
|
218
|
* <li>Path is when we come back from the last point back to the first the same way we got there.
|
219
|
* <li>Jump is when we go from first to last and then jump back to the first.
|
220
|
* </ul>
|
221
|
*
|
222
|
* @param mode {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
|
223
|
*/
|
224
|
|
225
|
public void setMode(int mode)
|
226
|
{
|
227
|
mMode = mode;
|
228
|
}
|
229
|
|
230
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
231
|
/**
|
232
|
* Returns the number of Static{1,2,3,4}Ds this Dynamic has been fed with.
|
233
|
*
|
234
|
* @return the number of Static{1,2,3,4}Ds we are currently interpolating through.
|
235
|
*/
|
236
|
public synchronized int getNumPoints()
|
237
|
{
|
238
|
return numPoints;
|
239
|
}
|
240
|
|
241
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
242
|
/**
|
243
|
* Controls how many times we want to interpolate.
|
244
|
* <p>
|
245
|
* Count equal to 1 means 'go from the first Static{1,2,3,4}D to the last and back'. Does not have to be an
|
246
|
* integer - i.e. count=1.5 would mean 'start at the first Point, go to the last, come back to the first,
|
247
|
* go to the last again and stop'.
|
248
|
* Count<=0 means 'go on interpolating indefinitely'.
|
249
|
*
|
250
|
* @param count the number of times we want to interpolate between our collection of Static{1,2,3,4}Ds.
|
251
|
*/
|
252
|
public void setCount(float count)
|
253
|
{
|
254
|
mCount = count;
|
255
|
}
|
256
|
|
257
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
258
|
/**
|
259
|
* Sets the time it takes to do one full interpolation.
|
260
|
*
|
261
|
* @param duration Time, in milliseconds, it takes to do one full interpolation, i.e. go from the first
|
262
|
* Point to the last and back.
|
263
|
*/
|
264
|
|
265
|
public void setDuration(long duration)
|
266
|
{
|
267
|
mDuration = duration;
|
268
|
}
|
269
|
|
270
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
271
|
/**
|
272
|
* Sets the 'smoothness' of interpolation.
|
273
|
* <p>
|
274
|
* When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
|
275
|
* Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
|
276
|
* up and slowing down, etc.
|
277
|
*
|
278
|
* @param noise The noise level. Permitted range: 0 <= noise <= 1.
|
279
|
*/
|
280
|
|
281
|
public synchronized void setNoise(float noise)
|
282
|
{
|
283
|
if( mNoise==0.0f && noise != 0.0f && vn==null )
|
284
|
{
|
285
|
vn = new Vector<>();
|
286
|
for(int i=0; i<numPoints; i++) vn.add(new VectorNoise(mDimension));
|
287
|
|
288
|
if( mDimension>=2 )
|
289
|
{
|
290
|
mFactor = new float[mDimension-1];
|
291
|
}
|
292
|
}
|
293
|
|
294
|
if( mNoise<0.0f ) mNoise = 0.0f;
|
295
|
if( mNoise>1.0f ) mNoise = 1.0f;
|
296
|
|
297
|
mNoise = noise;
|
298
|
}
|
299
|
|
300
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
301
|
// end of DistortedInterpolator
|
302
|
}
|