Project

General

Profile

« Previous | Next » 

Revision 553a9501

Added by Leszek Koltunski 21 days ago

transition the 'type' package

View differences:

src/main/java/org/distorted/library/type/Data1D.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
21
package org.distorted.library.type
22 22

  
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24
/**
25 25
 * Marker Interface implemented by Static1D and Dynamic1D.
26
 * <p>
26
 *
27 27
 * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
28 28
 */
29
public interface Data1D
30
  {
31
  boolean get(float[] buffer, int offset, long time, long step);
32
  }
29
interface Data1D
30
{
31
    fun get(buffer: FloatArray, offset: Int, time: Long, step: Long): Boolean
32
}
src/main/java/org/distorted/library/type/Data2D.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
21
package org.distorted.library.type
22 22

  
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24
/**
25 25
 * Marker Interface implemented by Static2D and Dynamic2D.
26
 * <p>
26
 *
27 27
 * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
28 28
 */
29
public interface Data2D
30
  {
31
  boolean get(float[] buffer, int offset, long time, long step);
32
  }
29
interface Data2D
30
{
31
    fun get(buffer: FloatArray, offset: Int, time: Long, step: Long): Boolean
32
}
src/main/java/org/distorted/library/type/Data3D.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
21
package org.distorted.library.type
22 22

  
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24
/**
25 25
 * Marker Interface implemented by Static3D and Dynamic3D.
26
 * <p>
26
 *
27 27
 * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
28 28
 */
29
public interface Data3D
30
  {
31
  boolean get(float[] buffer, int offset, long time, long step);
32
  }
29
interface Data3D
30
{
31
    fun get(buffer: FloatArray, offset: Int, time: Long, step: Long): Boolean
32
}
src/main/java/org/distorted/library/type/Data4D.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
21
package org.distorted.library.type
22 22

  
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24
/**
25 25
 * Marker Interface implemented by Static4D, Dynamic4D and DynamicQuat.
26
 * <p>
26
 *
27 27
 * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
28 28
 */
29
public interface Data4D
30
  {
31
  boolean get(float[] buffer, int offset, long time, long step);
32
  }
29
interface Data4D
30
{
31
    fun get(buffer: FloatArray, offset: Int, time: Long, step: Long): Boolean
32
}
src/main/java/org/distorted/library/type/Data5D.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
21
package org.distorted.library.type
22 22

  
23 23
///////////////////////////////////////////////////////////////////////////////////////////////////
24 24
/**
25 25
 * Marker Interface implemented by Static5D and Dynamic5D.
26
 * <p>
26
 *
27 27
 * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
28 28
 */
29
public interface Data5D
30
  {
31
  boolean get(float[] buffer, int offset, long time, long step);
32
  }
29
interface Data5D
30
{
31
    fun get(buffer: FloatArray, offset: Int, time: Long, step: Long): Boolean
32
}
src/main/java/org/distorted/library/type/Dynamic.kt
1
////////////////////////////////////////////////////////////////////////////////////////////////////
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2 2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3 3
//                                                                                               //
4 4
// This file is part of Distorted.                                                               //
......
18 18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19 19
///////////////////////////////////////////////////////////////////////////////////////////////////
20 20

  
21
package org.distorted.library.type;
22

  
23
import org.distorted.library.main.DistortedLibrary;
21
package org.distorted.library.type
24 22

  
25
import java.util.Random;
26
import java.util.Vector;
23
import org.distorted.library.main.DistortedLibrary
24
import java.util.Random
25
import java.util.Vector
26
import kotlin.math.sqrt
27 27

  
28 28
///////////////////////////////////////////////////////////////////////////////////////////////////
29 29
/** A class to interpolate between a list of Statics.
30
* <p><ul>
31
* <li>if there is only one Point, just return it.
32
* <li>if there are two Points, linearly bounce between them
33
* <li>if there are more, interpolate a path between them. Exact way we interpolate depends on the MODE.
34
* </ul>
30
*
31
*  if there is only one Point, just return it.
32
*  if there are two Points, linearly bounce between them
33
*  if there are more, interpolate a path between them. Exact way we interpolate depends on the MODE.
34
*
35 35
*/
36

  
37 36
// The way Interpolation between more than 2 Points is done:
38 37
// 
39 38
// Def: let V[i] = (V[i](x), V[i](y), V[i](z)) be the direction and speed (i.e. velocity) we have to
......
56 55
//
57 56
// and similarly Y(t) and Z(t).
58 57

  
59
public abstract class Dynamic
60
  {
61
  /**
62
   * Keep the speed of interpolation always changing. Time to cover one segment (distance between
63
   * two consecutive points) always the same. Smoothly interpolate the speed between two segments.
64
   */
65
  public static final int SPEED_MODE_SMOOTH            = 0;
66
  /**
67
   * Make each segment have constant speed. Time to cover each segment is still the same, thus the
68
   * speed will jump when passing through a point and then keep constant.
69
   */
70
  public static final int SPEED_MODE_SEGMENT_CONSTANT  = 1;
71
  /**
72
   * Have the speed be always, globally the same across all segments. Time to cover one segment will
73
   * thus generally no longer be the same.
74
   */
75
  public static final int SPEED_MODE_GLOBALLY_CONSTANT = 2;  // TODO: not supported yet
76

  
77
  /**
78
   * One revolution takes us from the first point to the last and back to first through the shortest path.
79
   */
80
  public static final int MODE_LOOP = 0; 
81
  /**
82
   * One revolution takes us from the first point to the last and back to first through the same path.
83
   */
84
  public static final int MODE_PATH = 1; 
85
  /**
86
   * One revolution takes us from the first point to the last and jumps straight back to the first point.
87
   */
88
  public static final int MODE_JUMP = 2; 
89

  
90
  /**
91
   * The default mode of access. When in this mode, we are able to call interpolate() with points in time
92
   * in any random order. This means one single Dynamic can be used in many effects simultaneously.
93
   * On the other hand, when in this mode, it is not possible to smoothly interpolate when mDuration suddenly
94
   * changes.
95
   */
96
  public static final int ACCESS_TYPE_RANDOM     = 0;
97
  /**
98
   * Set the mode to ACCESS_SEQUENTIAL if you need to change mDuration and you would rather have the Dynamic
99
   * keep on smoothly interpolating.
100
   * On the other hand, in this mode, a Dynamic can only be accessed in sequential manner, which means one
101
   * Dynamic can only be used in one effect at a time.
102
   */
103
  public static final int ACCESS_TYPE_SEQUENTIAL = 1;
104

  
105
  protected int mDimension;
106
  protected int numPoints;
107
  protected int mSegment;       // between which pair of points are we currently? (in case of PATH this is a bit complicated!)
108
  protected boolean cacheDirty; // VectorCache not up to date
109
  protected int mMode;          // LOOP, PATH or JUMP
110
  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
111
  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. 
112
  protected double mLastPos;
113
  protected int mAccessType;
114
  protected int mSpeedMode;
115
  protected float mTmpTime;
116
  protected int mTmpVec, mTmpSeg;
117

  
118
  protected class VectorNoise
119
    {
120
    float[][] n;
121

  
122
    VectorNoise()
123
      {
124
      n = new float[mDimension][NUM_NOISE];
125
      }
58
abstract class Dynamic
59
{
60
    companion object
61
    {
62
        /**
63
         * Keep the speed of interpolation always changing. Time to cover one segment (distance between
64
         * two consecutive points) always the same. Smoothly interpolate the speed between two segments.
65
         */
66
        const val SPEED_MODE_SMOOTH: Int = 0
67

  
68
        /**
69
         * Make each segment have constant speed. Time to cover each segment is still the same, thus the
70
         * speed will jump when passing through a point and then keep constant.
71
         */
72
        const val SPEED_MODE_SEGMENT_CONSTANT: Int = 1
73

  
74
        /**
75
         * Have the speed be always, globally the same across all segments. Time to cover one segment will
76
         * thus generally no longer be the same.
77
         */
78
        const val SPEED_MODE_GLOBALLY_CONSTANT: Int = 2 // TODO: not supported yet
79

  
80
        /**
81
         * One revolution takes us from the first point to the last and back to first through the shortest path.
82
         */
83
        const val MODE_LOOP: Int = 0
84

  
85
        /**
86
         * One revolution takes us from the first point to the last and back to first through the same path.
87
         */
88
        const val MODE_PATH: Int = 1
89

  
90
        /**
91
         * One revolution takes us from the first point to the last and jumps straight back to the first point.
92
         */
93
        const val MODE_JUMP: Int = 2
94

  
95
        /**
96
         * The default mode of access. When in this mode, we are able to call interpolate() with points in time
97
         * in any random order. This means one single Dynamic can be used in many effects simultaneously.
98
         * On the other hand, when in this mode, it is not possible to smoothly interpolate when mDuration suddenly
99
         * changes.
100
         */
101
        const val ACCESS_TYPE_RANDOM: Int = 0
102

  
103
        /**
104
         * Set the mode to ACCESS_SEQUENTIAL if you need to change mDuration and you would rather have the Dynamic
105
         * keep on smoothly interpolating.
106
         * On the other hand, in this mode, a Dynamic can only be accessed in sequential manner, which means one
107
         * Dynamic can only be used in one effect at a time.
108
         */
109
        const val ACCESS_TYPE_SEQUENTIAL: Int = 1
110

  
111
        private const val NUM_RATIO = 10 // we attempt to 'smooth out' the speed in each segment -
112

  
113
        // remember this many 'points' inside the Cache for each segment.
114
        protected val mTmpRatio: FloatArray = FloatArray(NUM_RATIO)
115

  
116
        private val mRnd = Random()
117
        private const val NUM_NOISE = 5 // used iff mNoise>0.0. Number of intermediary points between each pair of adjacent vectors
118
        private var mPausedTime: Long = 0
119

  
120
        @JvmStatic fun onPause() { mPausedTime = System.currentTimeMillis() }
121
    }
122

  
123
    var dimension: Int = 0
124
        protected set
125

  
126
    @get:Synchronized
127
    var numPoints: Int = 0
128
        protected set
129
    protected var mSegment: Int       = 0 // between which pair of points are we currently? (in case of PATH this is a bit complicated!)
130
    protected var cacheDirty: Boolean = false // VectorCache not up to date
131
    protected var mMode: Int          = 0 // LOOP, PATH or JUMP
132
    var duration: Long                = 0 // number of milliseconds it takes to do a full loop/path from first vector to the last and back to the first
133
    var count: Float                  = 0f // 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.
134
    protected var mLastPos: Double    = 0.0
135
    protected var mAccessType: Int    = 0
136
    protected var mSpeedMode: Int     = 0
137
    protected var mTmpTime: Float     = 0f
138
    protected var mTmpVec: Int        = 0
139
    protected var mTmpSeg: Int        = 0
140

  
141
    var convexity: Float
142
        get() = mConvexity
143
        set(convexity)
144
        {
145
            if (mConvexity!=convexity)
146
            {
147
                mConvexity = convexity
148
                cacheDirty = true
149
            }
150
        }
126 151

  
127
    void computeNoise()
128
      {
129
      n[0][0] = mRnd.nextFloat();
130
      for(int i=1; i<NUM_NOISE; i++) n[0][i] = n[0][i-1]+mRnd.nextFloat();
152
    val speedMode: Float
153
        get() = mSpeedMode.toFloat()
131 154

  
132
      float sum = n[0][NUM_NOISE-1] + mRnd.nextFloat();
155
    protected inner class VectorNoise
156
    internal constructor()
157
    {
158
        var n: Array<FloatArray> = Array(dimension) { FloatArray(NUM_NOISE) }
133 159

  
134
      for(int i=0; i<NUM_NOISE; i++)
160
        fun computeNoise()
135 161
        {
136
        n[0][i] /=sum;
137
        for(int j=1; j<mDimension; j++) n[j][i] = mRnd.nextFloat()-0.5f;
162
            n[0][0] = mRnd.nextFloat()
163
            for (i in 1..<NUM_NOISE) n[0][i] = n[0][i-1] + mRnd.nextFloat()
164

  
165
            val sum = n[0][NUM_NOISE-1] + mRnd.nextFloat()
166

  
167
            for (i in 0..<NUM_NOISE)
168
            {
169
                n[0][i] /= sum
170
                for (j in 1..<dimension) n[j][i] = mRnd.nextFloat()-0.5f
171
            }
138 172
        }
139
      }
140 173
    }
141 174

  
142
  protected Vector<VectorNoise> vn;
143
  protected float[] mFactor;
144
  protected float[] mNoise;
145
  protected float[][] baseV;
175
    protected var vn: Vector<VectorNoise>? = null
176
    protected lateinit var mFactor: FloatArray
177
    protected lateinit var mNoise: FloatArray
178
    protected lateinit var baseV: Array<FloatArray>
146 179

  
147
  ///////////////////////////////////////////////////////////////////////////////////////////////////
148
  // the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = a[0]*T^3 + b[0]*T^2 + c[0]*t + d[0]  etc.
149
  // (velocity) is the velocity vector.
150
  // (cached) is the original vector from vv (copied here so when interpolating we can see if it is
151
  // still valid and if not - rebuild the Cache
152

  
153
  protected class VectorCache
180
    ///////////////////////////////////////////////////////////////////////////////////////////////
181
    // the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = a[0]*T^3 + b[0]*T^2 + c[0]*t + d[0]  etc.
182
    // (velocity) is the velocity vector.
183
    // (cached) is the original vector from vv (copied here so when interpolating we can see if it is
184
    // still valid and if not - rebuild the Cache
185
    protected inner class VectorCache
186
    internal constructor()
154 187
    {
155
    float[] a;
156
    float[] b;
157
    float[] c;
158
    float[] d;
159
    float[] velocity;
160
    float[] cached;
161
    float[] path_ratio;
162

  
163
    VectorCache()
164
      {
165
      a = new float[mDimension];
166
      b = new float[mDimension];
167
      c = new float[mDimension];
168
      d = new float[mDimension];
169

  
170
      velocity   = new float[mDimension];
171
      cached     = new float[mDimension];
172
      path_ratio = new float[NUM_RATIO];
173
      }
188
        var a: FloatArray = FloatArray(dimension)
189
        var b: FloatArray = FloatArray(dimension)
190
        var c: FloatArray = FloatArray(dimension)
191
        var d: FloatArray = FloatArray(dimension)
192
        var velocity: FloatArray = FloatArray(dimension)
193
        var cached: FloatArray = FloatArray(dimension)
194
        var path_ratio: FloatArray = FloatArray(NUM_RATIO)
174 195
    }
175 196

  
176
  protected Vector<VectorCache> vc;
177
  protected VectorCache tmpCache1, tmpCache2;
178
  protected float mConvexity;
197
    protected var vc: Vector<VectorCache>? = null
198
    protected var mConvexity: Float = 0f
179 199

  
180
  private static final int NUM_RATIO = 10; // we attempt to 'smooth out' the speed in each segment -
181
                                           // remember this many 'points' inside the Cache for each segment.
200
    private lateinit var buf: FloatArray
201
    private lateinit var old: FloatArray
182 202

  
183
  protected static final float[] mTmpRatio = new float[NUM_RATIO];
203
    // where we randomize noise factors to make the way between the two vectors not so smooth.
204
    private var mStartTime: Long = 0
205
    private var mCorrectedTime: Long = 0
184 206

  
185
  private float[] buf;
186
  private float[] old;
187
  private static final Random mRnd = new Random();
188
  private static final int NUM_NOISE = 5; // used iff mNoise>0.0. Number of intermediary points between each pair of adjacent vectors
189
                                          // where we randomize noise factors to make the way between the two vectors not so smooth.
190
  private long mStartTime;
191
  private long mCorrectedTime;
192
  private static long mPausedTime;
207
    protected constructor()
193 208

  
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195
// hide this from Javadoc
196
  
197
  protected Dynamic()
209
    ///////////////////////////////////////////////////////////////////////////////////////////////
210
    protected constructor(duration: Int, count: Float, dimension: Int)
198 211
    {
212
        vc = Vector()
213
        vn = null
214
        numPoints = 0
215
        cacheDirty = false
216
        mMode = MODE_LOOP
217
        this.duration = duration.toLong()
218
        this.count = count
219
        this.dimension = dimension
220
        mSegment = -1
221
        mLastPos = -1.0
222
        mAccessType = ACCESS_TYPE_RANDOM
223
        mSpeedMode = SPEED_MODE_SMOOTH
224
        mConvexity = 1.0f
225
        mStartTime = -1
226
        mCorrectedTime = 0
199 227

  
228
        baseV = Array(this.dimension) { FloatArray(this.dimension) }
229
        buf = FloatArray(this.dimension)
230
        old = FloatArray(this.dimension)
200 231
    }
201 232

  
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

  
204
  protected Dynamic(int duration, float count, int dimension)
233
    ///////////////////////////////////////////////////////////////////////////////////////////////
234
    fun initDynamic()
205 235
    {
206
    vc         = new Vector<>();
207
    vn         = null;
208
    numPoints  = 0;
209
    cacheDirty = false;
210
    mMode      = MODE_LOOP;
211
    mDuration  = duration;
212
    mCount     = count;
213
    mDimension = dimension;
214
    mSegment   = -1;
215
    mLastPos   = -1;
216
    mAccessType= ACCESS_TYPE_RANDOM;
217
    mSpeedMode = SPEED_MODE_SMOOTH;
218
    mConvexity = 1.0f;
219
    mStartTime = -1;
220
    mCorrectedTime = 0;
221

  
222
    baseV      = new float[mDimension][mDimension];
223
    buf        = new float[mDimension];
224
    old        = new float[mDimension];
236
        mStartTime = -1
237
        mCorrectedTime = 0
225 238
    }
226 239

  
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

  
229
  void initDynamic()
240
    ///////////////////////////////////////////////////////////////////////////////////////////////
241
    protected fun computeSegmentAndTime(time: Float)
230 242
    {
231
    mStartTime = -1;
232
    mCorrectedTime = 0;
243
        when (mMode)
244
        {
245
            MODE_LOOP ->
246
                {
247
                    mTmpTime= time*numPoints
248
                    mTmpSeg = mTmpTime.toInt()
249
                    mTmpVec = mTmpSeg
250
                }
251

  
252
            MODE_PATH ->
253
                {
254
                    mTmpSeg = (2*time*(numPoints-1)).toInt()
255

  
256
                    if (time <= 0.5f)  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
257
                    {                  // and end position is slightly not equal to the end point => might not get autodeleted!
258
                        mTmpTime = 2*time*(numPoints-1)
259
                        mTmpVec = mTmpSeg
260
                    }
261
                    else
262
                    {
263
                        mTmpTime = 2 * (1 - time) * (numPoints - 1)
264
                        mTmpVec = 2 * numPoints - 3 - mTmpSeg
265
                    }
266
                }
267

  
268
            MODE_JUMP ->
269
                {
270
                    mTmpTime = time*(numPoints-1)
271
                    mTmpSeg = mTmpTime.toInt()
272
                    mTmpVec = mTmpSeg
273
                }
274

  
275
            else ->
276
                {
277
                    mTmpVec = 0
278
                    mTmpSeg = 0
279
                }
280
        }
233 281
    }
234 282

  
235
///////////////////////////////////////////////////////////////////////////////////////////////////
236

  
237
  public static void onPause()
283
    ///////////////////////////////////////////////////////////////////////////////////////////////
284
    private fun valueAtPoint(t: Float, cache: VectorCache): Float
238 285
    {
239
    mPausedTime = System.currentTimeMillis();
240
    }
286
        var tmp: Float
287
        var sum = 0.0f
241 288

  
242
///////////////////////////////////////////////////////////////////////////////////////////////////
289
        for (d in 0..<dimension)
290
        {
291
            tmp = (3*cache.a[d]*t + 2*cache.b[d])*t + cache.c[d]
292
            sum += tmp*tmp
293
        }
243 294

  
244
  protected void computeSegmentAndTime(float time)
245
    {
246
    switch(mMode)
247
      {
248
      case MODE_LOOP: mTmpTime= time*numPoints;
249
                      mTmpSeg = (int)mTmpTime;
250
                      mTmpVec = mTmpSeg;
251
                      break;
252
      case MODE_PATH: mTmpSeg = (int)(2*time*(numPoints-1));
253

  
254
                      if( time<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
255
                        {               // and end position is slightly not equal to the end point => might not get autodeleted!
256
                        mTmpTime = 2*time*(numPoints-1);
257
                        mTmpVec = mTmpSeg;
258
                        }
259
                      else
260
                        {
261
                        mTmpTime = 2*(1-time)*(numPoints-1);
262
                        mTmpVec  = 2*numPoints-3-mTmpSeg;
263
                        }
264
                      break;
265
      case MODE_JUMP: mTmpTime= time*(numPoints-1);
266
                      mTmpSeg = (int)mTmpTime;
267
                      mTmpVec = mTmpSeg;
268
                      break;
269
      default       : mTmpVec = 0;
270
                      mTmpSeg = 0;
271
      }
295
        return sqrt(sum.toDouble()).toFloat()
272 296
    }
273 297

  
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275

  
276
  private float valueAtPoint(float t, VectorCache cache)
298
    ///////////////////////////////////////////////////////////////////////////////////////////////
299
    protected fun smoothSpeed(time: Float, cache: VectorCache): Float
277 300
    {
278
    float tmp,sum = 0.0f;
279

  
280
    for(int d=0; d<mDimension; d++)
281
      {
282
      tmp = (3*cache.a[d]*t + 2*cache.b[d])*t + cache.c[d];
283
      sum += tmp*tmp;
284
      }
301
        val fndex = time*NUM_RATIO
302
        val index = fndex.toInt()
303
        val prev  = if (index==0) 0.0f else cache.path_ratio[index-1]
304
        val next  = cache.path_ratio[index]
285 305

  
286
    return (float)Math.sqrt(sum);
306
        return prev + (next-prev) * (fndex-index)
287 307
    }
288 308

  
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290

  
291
  protected float smoothSpeed(float time, VectorCache cache)
309
    ///////////////////////////////////////////////////////////////////////////////////////////////
310
    // First, compute the approx length of the segment from time=0 to time=(i+1)/NUM_TIME and store this
311
    // in cache.path_ratio[i]. Then the last path_ratio is the length from 0 to 1, i.e. the total length
312
    // of the segment.
313
    // We do this by computing the integral from 0 to 1 of sqrt( (dx/dt)^2 + (dy/dt)^2 ) (i.e. the length
314
    // of the segment) using the approx 'trapezoids' integration method.
315
    //
316
    // Then, for every i, divide path_ratio[i] by the total length to get the percentage of total path
317
    // length covered at time i. At this time, path_ratio[3] = 0.45 means 'at time 3/NUM_RATIO, we cover
318
    // 0.45 = 45% of the total length of the segment.
319
    //
320
    // Finally, invert this function (for quicker lookups in smoothSpeed) so that after this step,
321
    // path_ratio[3] = 0.45 means 'at 45% of the time, we cover 3/NUM_RATIO distance'.
322
    protected fun smoothOutSegment(cache: VectorCache)
292 323
    {
293
    float fndex = time*NUM_RATIO;
294
    int index = (int)fndex;
295
    float prev = index==0 ? 0.0f : cache.path_ratio[index-1];
296
    float next = cache.path_ratio[index];
324
        var vPrev: Float
325
        var sum = 0.0f
326
        var vNext = valueAtPoint(0.0f,cache)
297 327

  
298
    return prev + (next-prev)*(fndex-index);
299
    }
300

  
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302
// First, compute the approx length of the segment from time=0 to time=(i+1)/NUM_TIME and store this
303
// in cache.path_ratio[i]. Then the last path_ratio is the length from 0 to 1, i.e. the total length
304
// of the segment.
305
// We do this by computing the integral from 0 to 1 of sqrt( (dx/dt)^2 + (dy/dt)^2 ) (i.e. the length
306
// of the segment) using the approx 'trapezoids' integration method.
307
//
308
// Then, for every i, divide path_ratio[i] by the total length to get the percentage of total path
309
// length covered at time i. At this time, path_ratio[3] = 0.45 means 'at time 3/NUM_RATIO, we cover
310
// 0.45 = 45% of the total length of the segment.
311
//
312
// Finally, invert this function (for quicker lookups in smoothSpeed) so that after this step,
313
// path_ratio[3] = 0.45 means 'at 45% of the time, we cover 3/NUM_RATIO distance'.
314

  
315
  protected void smoothOutSegment(VectorCache cache)
316
    {
317
    float vPrev, sum = 0.0f;
318
    float vNext = valueAtPoint(0.0f,cache);
319

  
320
    for(int i=0; i<NUM_RATIO; i++)
321
      {
322
      vPrev = vNext;
323
      vNext = valueAtPoint( (float)(i+1)/NUM_RATIO,cache);
324
      sum += (vPrev+vNext);
325
      cache.path_ratio[i] = sum;
326
      }
327

  
328
    float total = cache.path_ratio[NUM_RATIO-1];
328
        for (i in 0..<NUM_RATIO)
329
        {
330
            vPrev = vNext
331
            vNext = valueAtPoint( (i+1).toFloat()/NUM_RATIO, cache )
332
            sum += (vPrev+vNext)
333
            cache.path_ratio[i] = sum
334
        }
329 335

  
330
    for(int i=0; i<NUM_RATIO; i++) cache.path_ratio[i] /= total;
336
        val total = cache.path_ratio[NUM_RATIO-1]
331 337

  
332
    int writeIndex = 0;
333
    float prev=0.0f, next, ratio= 1.0f/NUM_RATIO;
338
        for (i in 0..<NUM_RATIO) cache.path_ratio[i] /= total
334 339

  
335
    for(int readIndex=0; readIndex<NUM_RATIO; readIndex++)
336
      {
337
      next = cache.path_ratio[readIndex];
340
        var writeIndex = 0
341
        var prev = 0.0f
342
        var next: Float
343
        var ratio = 1.0f/NUM_RATIO
338 344

  
339
      while( prev<ratio && ratio<=next )
345
        for (readIndex in 0..<NUM_RATIO)
340 346
        {
341
        float a = (next-ratio)/(next-prev);
342
        mTmpRatio[writeIndex] = (readIndex+1-a)/NUM_RATIO;
343
        writeIndex++;
344
        ratio = (writeIndex+1.0f)/NUM_RATIO;
345
        }
347
            next = cache.path_ratio[readIndex]
346 348

  
347
      prev = next;
348
      }
349
            while (prev<ratio && ratio<=next)
350
            {
351
                val a = (next-ratio) / (next-prev)
352
                mTmpRatio[writeIndex] = (readIndex+1-a) / NUM_RATIO
353
                writeIndex++
354
                ratio = (writeIndex+1.0f) / NUM_RATIO
355
            }
349 356

  
350
    System.arraycopy(mTmpRatio, 0, cache.path_ratio, 0, NUM_RATIO);
351
    }
352

  
353
///////////////////////////////////////////////////////////////////////////////////////////////////
357
            prev = next
358
        }
354 359

  
355
  protected float noise(float time,int vecNum)
356
    {
357
    float lower, upper, len;
358
    float d = time*(NUM_NOISE+1);
359
    int index = (int)d;
360
    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
361
    VectorNoise tmpN = vn.elementAt(vecNum);
362

  
363
    float t = d-index;
364
    t = t*t*(3-2*t);
365

  
366
    switch(index)
367
      {
368
      case 0        : for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][0]*t;
369
                      return time + mNoise[0]*(d*tmpN.n[0][0]-time);
370
      case NUM_NOISE: for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][NUM_NOISE-1]*(1-t);
371
                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
372
                      lower = len + mNoise[0]*(tmpN.n[0][NUM_NOISE-1]-len);
373
                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
374
      default       : float ya,yb;
375

  
376
                      for(int i=0;i<mDimension-1;i++)
377
                        {
378
                        yb = tmpN.n[i+1][index  ];
379
                        ya = tmpN.n[i+1][index-1];
380
                        mFactor[i] = mNoise[i+1]*((yb-ya)*t+ya);
381
                        }
382

  
383
                      len = ((float)index)/(NUM_NOISE+1);
384
                      lower = len + mNoise[0]*(tmpN.n[0][index-1]-len);
385
                      len = ((float)index+1)/(NUM_NOISE+1);
386
                      upper = len + mNoise[0]*(tmpN.n[0][index  ]-len);
387

  
388
                      return (upper-lower)*(d-index) + lower;
389
      }
360
        System.arraycopy(mTmpRatio, 0, cache.path_ratio, 0, NUM_RATIO)
390 361
    }
391 362

  
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393
// debugging only
394

  
395
  private void printBase(String str)
363
    ///////////////////////////////////////////////////////////////////////////////////////////////
364
    protected fun noise(time: Float, vecNum: Int): Float
396 365
    {
397
    String s;
398
    float t;
366
        val lower: Float
367
        val upper: Float
368
        var len: Float
369
        val d = time*(NUM_NOISE+1)
370
        var index = d.toInt()
371
        if (index >= NUM_NOISE+1) index = NUM_NOISE
372
        val tmpN = vn!!.elementAt(vecNum)
399 373

  
400
    for(int i=0; i<mDimension; i++)
401
      {
402
      s = "";
374
        var t = d-index
375
        t = t*t*(3-2*t)
403 376

  
404
      for(int j=0; j<mDimension; j++)
377
        when (index)
405 378
        {
406
        t = ((int)(1000*baseV[i][j]))/(1000.0f);
407
        s+=(" "+t);
379
            0 ->
380
                {
381
                    var i = 0
382
                    while (i < dimension-1)
383
                    {
384
                        mFactor[i] = mNoise[i+1] * tmpN.n[i+1][0] * t
385
                        i++
386
                    }
387
                    return time + mNoise[0] * (d*tmpN.n[0][0]-time)
388
                }
389

  
390
            NUM_NOISE ->
391
                {
392
                    var i = 0
393
                    while (i < dimension - 1)
394
                    {
395
                        mFactor[i] = mNoise[i+1] * tmpN.n[i+1][NUM_NOISE-1] * (1-t)
396
                        i++
397
                    }
398

  
399
                    len = (NUM_NOISE.toFloat()) / (NUM_NOISE+1)
400
                    lower = len + mNoise[0] * (tmpN.n[0][NUM_NOISE-1] - len)
401
                    return (1.0f-lower) * (d-NUM_NOISE) + lower
402
                }
403

  
404
            else ->
405
                {
406
                    var ya: Float
407
                    var yb: Float
408
                    var i = 0
409

  
410
                    while (i < dimension-1)
411
                    {
412
                        yb = tmpN.n[i+1][index]
413
                        ya = tmpN.n[i+1][index-1]
414
                        mFactor[i] = mNoise[i+1] * ((yb-ya) * t+ya)
415
                        i++
416
                    }
417

  
418
                    len   = (index.toFloat()) / (NUM_NOISE+1)
419
                    lower = len + mNoise[0] * (tmpN.n[0][index-1] - len)
420
                    len   = (index.toFloat()+1) / (NUM_NOISE+1)
421
                    upper = len + mNoise[0] * (tmpN.n[0][index] - len)
422

  
423
                return (upper-lower)*(d-index) + lower
424
                }
408 425
        }
409
      DistortedLibrary.logMessage("Dynamic: "+str+" base "+i+" : " + s);
410
      }
411 426
    }
412 427

  
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414
// debugging only
415

  
416
  @SuppressWarnings("unused")
417
  private void checkBase()
428
    ///////////////////////////////////////////////////////////////////////////////////////////////
429
    // debugging only
430
    private fun printBase(str: String)
418 431
    {
419
    float tmp, cosA;
420
    float[] len= new float[mDimension];
421
    boolean error=false;
422

  
423
    for(int i=0; i<mDimension; i++)
424
      {
425
      len[i] = 0.0f;
426

  
427
      for(int k=0; k<mDimension; k++)
428
        {
429
        len[i] += baseV[i][k]*baseV[i][k];
430
        }
432
        var s: String
433
        var t: Float
431 434

  
432
      if( len[i] == 0.0f || len[0]/len[i] < 0.95f || len[0]/len[i]>1.05f )
435
        for (i in 0..<dimension)
433 436
        {
434
        DistortedLibrary.logMessage("Dynamic: length of vector "+i+" : "+Math.sqrt(len[i]));
435
        error = true;
437
            s = ""
438

  
439
            for (j in 0..<dimension)
440
            {
441
                t = ((1000*baseV[i][j]).toInt()) / (1000.0f)
442
                s += (" $t")
443
            }
444
            DistortedLibrary.logMessage("Dynamic: $str base $i : $s")
436 445
        }
437
      }
438

  
439
    for(int i=0; i<mDimension; i++)
440
      for(int j=i+1; j<mDimension; j++)
441
        {
442
        tmp = 0.0f;
443

  
444
        for(int k=0; k<mDimension; k++)
445
          {
446
          tmp += baseV[i][k]*baseV[j][k];
447
          }
448

  
449
        cosA = ( (len[i]==0.0f || len[j]==0.0f) ? 0.0f : tmp/(float)Math.sqrt(len[i]*len[j]));
450

  
451
        if( cosA > 0.05f || cosA < -0.05f )
452
          {
453
          DistortedLibrary.logMessage("Dynamic: cos angle between vectors "+i+" and "+j+" : "+cosA);
454
          error = true;
455
          }
456
        }
457

  
458
    if( error ) printBase("");
459 446
    }
460 447

  
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462

  
463
  int getNext(int curr, float time)
448
    ///////////////////////////////////////////////////////////////////////////////////////////////
449
    // debugging only
450
    @Suppress("unused")
451
    private fun checkBase()
464 452
    {
465
    switch(mMode)
466
      {
467
      case MODE_LOOP: return curr==numPoints-1 ? 0:curr+1;
468
      case MODE_PATH: return time<0.5f ? (curr+1) : (curr==0 ? 1 : curr-1);
469
      case MODE_JUMP: return curr==numPoints-1 ? 1:curr+1;
470
      default       : return 0;
471
      }
472
    }
473

  
474
///////////////////////////////////////////////////////////////////////////////////////////////////
453
        var tmp: Float
454
        var cosA: Float
455
        val len = FloatArray(dimension)
456
        var error = false
475 457

  
476
  private void checkAngle(int index)
477
    {
478
    float cosA = 0.0f;
458
        for (i in 0..<dimension)
459
        {
460
            len[i] = 0.0f
461
            for (k in 0..<dimension) len[i] += baseV[i][k]*baseV[i][k]
462

  
463
            if (len[i]==0.0f || len[0]/len[i] < 0.95f || len[0]/len[i] > 1.05f)
464
            {
465
                DistortedLibrary.logMessage("Dynamic: length of vector $i : " + sqrt(len[i].toDouble()) )
466
                error = true
467
            }
468
        }
479 469

  
480
    for(int k=0;k<mDimension; k++)
481
      cosA += baseV[index][k]*old[k];
470
        for (i in 0..<dimension)
471
            for (j in i+1..<dimension)
472
            {
473
                tmp = 0.0f
474
                for (k in 0..<dimension) tmp += baseV[i][k]*baseV[j][k]
482 475

  
483
    if( cosA<0.0f )
484
      {
485
      for(int j=0; j<mDimension; j++)
486
        baseV[index][j] = -baseV[index][j];
487
      }
488
    }
476
                cosA = (if (len[i]==0.0f || len[j]==0.0f) 0.0f else tmp / sqrt((len[i]*len[j]).toDouble()).toFloat())
489 477

  
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491
// helper function in case we are interpolating through exactly 2 points
478
                if (cosA>0.05f || cosA<-0.05f)
479
                {
480
                    DistortedLibrary.logMessage("Dynamic: cos angle between vectors $i and $j : $cosA")
481
                    error = true
482
                }
483
            }
492 484

  
493
  protected void computeOrthonormalBase2(Static curr, Static next)
494
    {
495
    switch(mDimension)
496
      {
497
      case 1: Static1D curr1 = (Static1D)curr;
498
              Static1D next1 = (Static1D)next;
499
              baseV[0][0] = (next1.x-curr1.x);
500
              break;
501
      case 2: Static2D curr2 = (Static2D)curr;
502
              Static2D next2 = (Static2D)next;
503
              baseV[0][0] = (next2.x-curr2.x);
504
              baseV[0][1] = (next2.y-curr2.y);
505
              break;
506
      case 3: Static3D curr3 = (Static3D)curr;
507
              Static3D next3 = (Static3D)next;
508
              baseV[0][0] = (next3.x-curr3.x);
509
              baseV[0][1] = (next3.y-curr3.y);
510
              baseV[0][2] = (next3.z-curr3.z);
511
              break;
512
      case 4: Static4D curr4 = (Static4D)curr;
513
              Static4D next4 = (Static4D)next;
514
              baseV[0][0] = (next4.x-curr4.x);
515
              baseV[0][1] = (next4.y-curr4.y);
516
              baseV[0][2] = (next4.z-curr4.z);
517
              baseV[0][3] = (next4.w-curr4.w);
518
              break;
519
      case 5: Static5D curr5 = (Static5D)curr;
520
              Static5D next5 = (Static5D)next;
521
              baseV[0][0] = (next5.x-curr5.x);
522
              baseV[0][1] = (next5.y-curr5.y);
523
              baseV[0][2] = (next5.z-curr5.z);
524
              baseV[0][3] = (next5.w-curr5.w);
525
              baseV[0][4] = (next5.v-curr5.v);
526
              break;
527
      default: throw new RuntimeException("Unsupported dimension");
528
      }
529

  
530
    if( baseV[0][0] == 0.0f )
531
      {
532
      baseV[1][0] = 1.0f;
533
      baseV[1][1] = 0.0f;
534
      }
535
    else
536
      {
537
      baseV[1][0] = 0.0f;
538
      baseV[1][1] = 1.0f;
539
      }
540

  
541
    for(int i=2; i<mDimension; i++)
542
      {
543
      baseV[1][i] = 0.0f;
544
      }
545

  
546
    computeOrthonormalBase();
485
        if (error) printBase("")
547 486
    }
548 487

  
549
///////////////////////////////////////////////////////////////////////////////////////////////////
550
// helper function in case we are interpolating through more than 2 points
551

  
552
  protected void computeOrthonormalBaseMore(float time,VectorCache vc)
488
    ///////////////////////////////////////////////////////////////////////////////////////////////
489
    fun getNext(curr: Int, time: Float): Int
553 490
    {
554
    for(int i=0; i<mDimension; i++)
555
      {
556
      baseV[0][i] = (3*vc.a[i]*time+2*vc.b[i])*time+vc.c[i];   // first derivative, i.e. velocity vector
557
      old[i]      = baseV[1][i];
558
      baseV[1][i] =  6*vc.a[i]*time+2*vc.b[i];                 // second derivative,i.e. acceleration vector
559
      }
560

  
561
    computeOrthonormalBase();
491
        return when (mMode)
492
        {
493
            MODE_LOOP -> if (curr == numPoints-1) 0        else curr+1
494
            MODE_PATH -> if (time < 0.5f        ) (curr+1) else (if (curr==0) 1 else curr-1)
495
            MODE_JUMP -> if (curr == numPoints-1) 1        else curr+1
496
            else      -> 0
497
        }
562 498
    }
563 499

  
564
///////////////////////////////////////////////////////////////////////////////////////////////////
565
// When this function gets called, baseV[0] and baseV[1] should have been filled with two mDimension-al
566
// vectors. This function then fills the rest of the baseV array with a mDimension-al Orthonormal base.
567
// (mDimension-2 vectors, pairwise orthogonal to each other and to the original 2). The function always
568
// leaves base[0] alone but generally speaking must adjust base[1] to make it orthogonal to base[0]!
569
// The whole baseV is then used to compute Noise.
570
//
571
// When computing noise of a point travelling along a N-dimensional path, there are three cases:
572
// a) we may be interpolating through 1 point, i.e. standing in place - nothing to do in this case
573
// b) we may be interpolating through 2 points, i.e. travelling along a straight line between them -
574
//    then pass the velocity vector in baseV[0] and anything linearly independent in base[1].
575
//    The output will then be discontinuous in dimensions>2 (sad corollary from the Hairy Ball Theorem)
576
//    but we don't care - we are travelling along a straight line, so velocity (aka baseV[0]!) does
577
//    not change.
578
// c) we may be interpolating through more than 2 points. Then interpolation formulas ensure the path
579
//    will never be a straight line, even locally -> we can pass in baseV[0] and baseV[1] the velocity
580
//    and the acceleration (first and second derivatives of the path) which are then guaranteed to be
581
//    linearly independent. Then we can ensure this is continuous in dimensions <=4. This leaves
582
//    dimension 5 (ATM WAVE is 5-dimensional) discontinuous -> WAVE will suffer from chaotic noise.
583
//
584
// Bear in mind here the 'normal' in 'orthonormal' means 'length equal to the length of the original
585
// velocity vector' (rather than the standard 1)
586

  
587
  protected void computeOrthonormalBase()
500
    ///////////////////////////////////////////////////////////////////////////////////////////////
501
    private fun checkAngle(index: Int)
588 502
    {
589
    int last_non_zero=-1;
590
    float tmp;
591

  
592
    for(int i=0; i<mDimension; i++)
593
      if( baseV[0][i] != 0.0f )
594
        last_non_zero=i;
595

  
596
    if( last_non_zero==-1 )                                               ///
597
      {                                                                   //  velocity is the 0 vector -> two
598
      for(int i=0; i<mDimension-1; i++)                                   //  consecutive points we are interpolating
599
        for(int j=0; j<mDimension; j++)                                   //  through are identical -> no noise,
600
          baseV[i+1][j]= 0.0f;                                            //  set the base to 0 vectors.
601
      }                                                                   ///
602
    else
603
      {
604
      for(int i=1; i<mDimension; i++)                                     /// One iteration computes baseV[i][*]
605
        {                                                                 //  (aka b[i]), the i-th orthonormal vector.
606
        buf[i-1]=0.0f;                                                    //
607
                                                                          //  We can use (modified!) Gram-Schmidt.
608
        for(int k=0; k<mDimension; k++)                                   //
609
          {                                                               //
610
          if( i>=2 )                                                      //  b[0] = b[0]
611
            {                                                             //  b[1] = b[1] - (<b[1],b[0]>/<b[0],b[0]>)*b[0]
612
            old[k] = baseV[i][k];                                         //  b[2] = b[2] - (<b[2],b[0]>/<b[0],b[0]>)*b[0] - (<b[2],b[1]>/<b[1],b[1]>)*b[1]
613
            baseV[i][k]= (k==i-(last_non_zero>=i?1:0)) ? 1.0f : 0.0f;     //  b[3] = b[3] - (<b[3],b[0]>/<b[0],b[0]>)*b[0] - (<b[3],b[1]>/<b[1],b[1]>)*b[1] - (<b[3],b[2]>/<b[2],b[2]>)*b[2]
614
            }                                                             //  (...)
615
                                                                          //  then b[i] = b[i] / |b[i]|  ( Here really b[i] = b[i] / (|b[0]|/|b[i]|)
616
          tmp = baseV[i-1][k];                                            //
617
          buf[i-1] += tmp*tmp;                                            //
618
          }                                                               //
619
                                                                          //
620
        for(int j=0; j<i; j++)                                            //
621
          {                                                               //
622
          tmp = 0.0f;                                                     //
623
          for(int k=0;k<mDimension; k++) tmp += baseV[i][k]*baseV[j][k];  //
624
          tmp /= buf[j];                                                  //
625
          for(int k=0;k<mDimension; k++) baseV[i][k] -= tmp*baseV[j][k];  //
626
          }                                                               //
627
                                                                          //
628
        checkAngle(i);                                                    //
629
        }                                                                 /// end compute baseV[i][*]
630

  
631
      buf[mDimension-1]=0.0f;                                             /// Normalize
632
      for(int k=0; k<mDimension; k++)                                     //
633
        {                                                                 //
634
        tmp = baseV[mDimension-1][k];                                     //
635
        buf[mDimension-1] += tmp*tmp;                                     //
636
        }                                                                 //
637
                                                                          //
638
      for(int i=1; i<mDimension; i++)                                     //
639
        {                                                                 //
640
        tmp = (float)Math.sqrt(buf[0]/buf[i]);                            //
641
        for(int k=0;k<mDimension; k++) baseV[i][k] *= tmp;                //
642
        }                                                                 /// End Normalize
643
      }
644
    }
503
        var cosA = 0.0f
504
        for (k in 0..<dimension) cosA += baseV[index][k]*old[k]
645 505

  
646
///////////////////////////////////////////////////////////////////////////////////////////////////
647

  
648
  abstract void interpolate(float[] buffer, int offset, float time);
649

  
650
///////////////////////////////////////////////////////////////////////////////////////////////////
651
// PUBLIC API
652
///////////////////////////////////////////////////////////////////////////////////////////////////
653

  
654
/**
655
 * Sets the mode of the interpolation to Loop, Path or Jump.
656
 * <ul>
657
 * <li>Loop is when we go from the first point all the way to the last, and the back to the first through 
658
 * the shortest way.
659
 * <li>Path is when we come back from the last point back to the first the same way we got there.
660
 * <li>Jump is when we go from first to last and then jump straight back to the first.
661
 * </ul>
662
 * 
663
 * @param mode {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
664
 */
665
  public void setMode(int mode)
666
    {
667
    mMode = mode;  
506
        if (cosA < 0.0f)
507
            for (j in 0..<dimension) baseV[index][j] = -baseV[index][j]
668 508
    }
669 509

  
670
///////////////////////////////////////////////////////////////////////////////////////////////////
671
/**
672
 * Returns the number of Points this Dynamic has been fed with.
673
 *   
674
 * @return the number of Points we are currently interpolating through.
675
 */
676
  public synchronized int getNumPoints()
510
    ///////////////////////////////////////////////////////////////////////////////////////////////
511
    // helper function in case we are interpolating through exactly 2 points
512
    protected fun computeOrthonormalBase2(curr: Static, next: Static)
677 513
    {
678
    return numPoints;  
679
    }
514
        when (dimension)
515
        {
516
            1 ->
517
                {
518
                    val curr1 = curr as Static1D
519
                    val next1 = next as Static1D
520
                    baseV[0][0] = (next1.x - curr1.x)
521
                }
522

  
523
            2 ->
524
                {
525
                    val curr2 = curr as Static2D
526
                    val next2 = next as Static2D
527
                    baseV[0][0] = (next2.x - curr2.x)
528
                    baseV[0][1] = (next2.y - curr2.y)
529
                }
530

  
531
            3 ->
532
                {
533
                    val curr3 = curr as Static3D
534
                    val next3 = next as Static3D
535
                    baseV[0][0] = (next3.x - curr3.x)
536
                    baseV[0][1] = (next3.y - curr3.y)
537
                    baseV[0][2] = (next3.z - curr3.z)
538
                }
539

  
540
            4 ->
541
                {
542
                    val curr4 = curr as Static4D
543
                    val next4 = next as Static4D
544
                    baseV[0][0] = (next4.x - curr4.x)
545
                    baseV[0][1] = (next4.y - curr4.y)
546
                    baseV[0][2] = (next4.z - curr4.z)
547
                    baseV[0][3] = (next4.w - curr4.w)
548
                }
549

  
550
            5 ->
551
                {
552
                    val curr5 = curr as Static5D
553
                    val next5 = next as Static5D
554
                    baseV[0][0] = (next5.x - curr5.x)
555
                    baseV[0][1] = (next5.y - curr5.y)
556
                    baseV[0][2] = (next5.z - curr5.z)
557
                    baseV[0][3] = (next5.w - curr5.w)
558
                    baseV[0][4] = (next5.v - curr5.v)
559
                }
560

  
561
            else -> throw RuntimeException("Unsupported dimension")
562
        }
680 563

  
681
///////////////////////////////////////////////////////////////////////////////////////////////////
682
/**
683
 * Sets how many revolutions we want to do.
684
 * <p>
685
 * Does not have to be an integer. What constitutes 'one revolution' depends on the MODE:
686
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
687
 * Count<=0 means 'go on interpolating indefinitely'.
688
 * 
689
 * @param count the number of times we want to interpolate between our collection of Points.
690
 */
691
  public void setCount(float count)
692
    {
693
    mCount = count;  
694
    }
564
        if (baseV[0][0] == 0.0f)
565
        {
566
            baseV[1][0] = 1.0f
567
            baseV[1][1] = 0.0f
568
        }
569
        else
570
        {
571
            baseV[1][0] = 0.0f
572
            baseV[1][1] = 1.0f
573
        }
695 574

  
696
///////////////////////////////////////////////////////////////////////////////////////////////////
697
/**
698
 * Return the number of revolutions this Dynamic will make.
699
 * What constitutes 'one revolution' depends on the MODE:
700
 * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
701
 *
702
 * @return the number revolutions this Dynamic will make.
703
 */
704
  public float getCount()
705
    {
706
    return mCount;
707
    }
575
        for (i in 2..<dimension) baseV[1][i] = 0.0f
708 576

  
709
///////////////////////////////////////////////////////////////////////////////////////////////////
710
/**
711
 * Start running from the beginning again.
712
 *
713
 * If a Dynamic has been used already, and we want to use it again and start interpolating from the
714
 * first Point, first we need to reset it using this method.
715
 */
716
  public void resetToBeginning()
717
    {
718
    mStartTime = -1;
577
        computeOrthonormalBase()
719 578
    }
720 579

  
721
///////////////////////////////////////////////////////////////////////////////////////////////////
722
/**
723
 * @param duration Number of milliseconds one revolution will take.
724
 *                 What constitutes 'one revolution' depends on the MODE:
725
 *                 {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
726
 */
727
  public void setDuration(long duration)
580
    ///////////////////////////////////////////////////////////////////////////////////////////////
581
    // helper function in case we are interpolating through more than 2 points
582
    protected fun computeOrthonormalBaseMore(time: Float, vc: VectorCache)
728 583
    {
729
    mDuration = duration;
730
    }
584
        for (i in 0..<dimension)
585
        {
586
            baseV[0][i] = (3*vc.a[i]*time + 2*vc.b[i])*time + vc.c[i] // first derivative, i.e. velocity vector
587
            old[i] = baseV[1][i]
588
            baseV[1][i] = 6*vc.a[i]*time + 2*vc.b[i] // second derivative,i.e. acceleration vector
589
        }
731 590

  
732
///////////////////////////////////////////////////////////////////////////////////////////////////
733
/**
734
 * @return Number of milliseconds one revolution will take.
735
 */
736
  public long getDuration()
737
    {
738
    return mDuration;
591
        computeOrthonormalBase()
592
    }
593

  
594
    ///////////////////////////////////////////////////////////////////////////////////////////////
595
    // When this function gets called, baseV[0] and baseV[1] should have been filled with two mDimension-al
596
    // vectors. This function then fills the rest of the baseV array with a mDimension-al Orthonormal base.
597
    // (mDimension-2 vectors, pairwise orthogonal to each other and to the original 2). The function always
598
    // leaves base[0] alone but generally speaking must adjust base[1] to make it orthogonal to base[0]!
599
    // The whole baseV is then used to compute Noise.
600
    //
601
    // When computing noise of a point travelling along a N-dimensional path, there are three cases:
602
    // a) we may be interpolating through 1 point, i.e. standing in place - nothing to do in this case
603
    // b) we may be interpolating through 2 points, i.e. travelling along a straight line between them -
604
    //    then pass the velocity vector in baseV[0] and anything linearly independent in base[1].
605
    //    The output will then be discontinuous in dimensions>2 (sad corollary from the Hairy Ball Theorem)
606
    //    but we don't care - we are travelling along a straight line, so velocity (aka baseV[0]!) does
607
    //    not change.
608
    // c) we may be interpolating through more than 2 points. Then interpolation formulas ensure the path
609
    //    will never be a straight line, even locally -> we can pass in baseV[0] and baseV[1] the velocity
610
    //    and the acceleration (first and second derivatives of the path) which are then guaranteed to be
611
    //    linearly independent. Then we can ensure this is continuous in dimensions <=4. This leaves
612
    //    dimension 5 (ATM WAVE is 5-dimensional) discontinuous -> WAVE will suffer from chaotic noise.
613
    //
614
    // Bear in mind here the 'normal' in 'orthonormal' means 'length equal to the length of the original
615
    // velocity vector' (rather than the standard 1)
616
    protected fun computeOrthonormalBase()
617
    {
618
        var lastnonzero = -1
619
        var tmp: Float
620

  
621
        for (i in 0..<dimension)
622
            if (baseV[0][i] != 0.0f) lastnonzero = i
623

  
624
        if (lastnonzero == -1)
625
        {                                   //  velocity is the 0 vector -> two
626
            for (i in 0..<dimension-1)    //  consecutive points we are interpolating
627
                for (j in 0..<dimension)  //  through are identical -> no noise,
628
                    baseV[i+1][j] = 0.0f    //  set the base to 0 vectors.
629
        }
630
        else
631
        {
632
            for (i in 1..<dimension)                                                             // One iteration computes baseV[i][*]
633
            {                                                                                      //  (aka b[i]), the i-th orthonormal vector.
634
                buf[i-1] = 0.0f                                                                    //
635
                                                                                                   //  We can use (modified!) Gram-Schmidt.
636
                for (k in 0..<dimension)                                                         //
637
                {                                                                                  //
638
                    if (i>=2)                                                                      //  b[0] = b[0]
639
                    {                                                                              //  b[1] = b[1] - (<b[1],b[0]>/<b[0],b[0]>)*b[0]
640
                        old[k] = baseV[i][k]                                                       //  b[2] = b[2] - (<b[2],b[0]>/<b[0],b[0]>)*b[0] - (<b[2],b[1]>/<b[1],b[1]>)*b[1]
641
                        baseV[i][k] = if (k== i - (if (lastnonzero >= i) 1 else 0)) 1.0f else 0.0f //  b[3] = b[3] - (<b[3],b[0]>/<b[0],b[0]>)*b[0] - (<b[3],b[1]>/<b[1],b[1]>)*b[1] - (<b[3],b[2]>/<b[2],b[2]>)*b[2]
642
                    }                                                                              //  (...)
643
                                                                                                   //  then b[i] = b[i] / |b[i]|  ( Here really b[i] = b[i] / (|b[0]|/|b[i]|)
644
                    tmp = baseV[i-1][k]                                                            //
645
                    buf[i-1] += tmp*tmp                                                            //
646
                }                                                                                  //
647
                                                                                                   //
648
                for (j in 0..<i)                                                                 //
649
                {                                                                                  //
650
                    tmp = 0.0f                                                                     //
651
                    for (k in 0..<dimension) tmp += baseV[i][k]*baseV[j][k]                      //
652
                    tmp /= buf[j]                                                                  //
653
                    for (k in 0..<dimension) baseV[i][k] -= tmp*baseV[j][k]                      //
654
                }                                                                                  //
655
                                                                                                   //
656
                checkAngle(i)                                                                      //
657
            }                                                                                      //
658
                                                                                                   // end compute baseV[i][*]
659
            buf[dimension-1] = 0.0f
660
                                                                                                   // Normalize
661
            for (k in 0..<dimension)                                                             //
662
            {                                                                                      //
663
                tmp = baseV[dimension-1][k]                                                        //
664
                buf[dimension-1] += tmp*tmp                                                        //
665
            }                                                                                      //
666
                                                                                                   //
667
            for (i in 1..<dimension)                                                             //
668
            {                                                                                      //
669
                tmp = sqrt((buf[0] / buf[i]).toDouble()).toFloat()                                 //
670
                for (k in 0..<dimension) baseV[i][k] *= tmp                                      //
671
            }                                                                                      // End Normalize
672
        }
739 673
    }
740 674

  
741
///////////////////////////////////////////////////////////////////////////////////////////////////
742
/**
743
 * @param convexity If set to the default (1.0f) then interpolation between 4 points
744
 *                  (1,0) (0,1) (-1,0) (0,-1) will be the natural circle centered at (0,0) with radius 1.
745
 *                  The less it is, the less convex the circle becomes, ultimately when convexity=0.0f
746
 *                  then the interpolation shape will be straight lines connecting the four points.
747
 *                  Further setting this to negative values will make the shape concave.
748
 *                  Valid values: all floats. (although probably only something around (0,2) actually
749
 *                  makes sense)
750
 */
751
  public void setConvexity(float convexity)
752
    {
753
    if( mConvexity!=convexity )
754
      {
755
      mConvexity = convexity;
756
      cacheDirty = true;
757
      }
675
    ///////////////////////////////////////////////////////////////////////////////////////////////
676
    abstract fun interpolate(buffer: FloatArray, offset: Int, tm: Float)
677

  
678
    ///////////////////////////////////////////////////////////////////////////////////////////////
679
    // PUBLIC API
680
    ///////////////////////////////////////////////////////////////////////////////////////////////
681
    /** Sets the mode of the interpolation to Loop, Path or Jump.
682
    *
683
    *  Loop is when we go from the first point all the way to the last, and the back to the first through the shortest way.
684
    *  Path is when we come back from the last point back to the first the same way we got there.
685
    *  Jump is when we go from first to last and then jump straight back to the first.
686
    *
687
    * @param mode [Dynamic.MODE_LOOP], [Dynamic.MODE_PATH] or [Dynamic.MODE_JUMP].
688
    */
689
    fun setMode(mode: Int) { mMode = mode }
690

  
691
    ///////////////////////////////////////////////////////////////////////////////////////////////
692
    /** Start running from the beginning again.
693
    *
694
    * If a Dynamic has been used already, and we want to use it again and start interpolating from the
695
    * first Point, first we need to reset it using this method.
696
    */
697
    fun resetToBeginning() { mStartTime = -1 }
698

  
699
    ///////////////////////////////////////////////////////////////////////////////////////////////
700
    /** Sets the access type this Dynamic will be working in.
701
    *
702
    * @param type [Dynamic.ACCESS_TYPE_RANDOM] or [Dynamic.ACCESS_TYPE_SEQUENTIAL].
703
    */
704
    fun setAccessType(type: Int)
705
    {
706
        mAccessType = type
707
        mLastPos = -1.0
708
    }
709

  
710
    ///////////////////////////////////////////////////////////////////////////////////////////////
711
    /** Sets the way we compute the interpolation speed.
712
    *
713
    * @param mode [Dynamic.SPEED_MODE_SMOOTH] or [Dynamic.SPEED_MODE_SEGMENT_CONSTANT] or
714
    * [Dynamic.SPEED_MODE_GLOBALLY_CONSTANT]
715
    */
716
    fun setSpeedMode(mode: Int)
717
    {
718
        if (mSpeedMode!=mode)
719
        {
720
            if (mSpeedMode==SPEED_MODE_SMOOTH)
721
                for (i in 0..<numPoints) smoothOutSegment(vc!!.elementAt(i))
722
            mSpeedMode = mode
723
        }
758 724
    }
759 725

  
760
///////////////////////////////////////////////////////////////////////////////////////////////////
761
/**
762
 * @return See {@link Dynamic#setConvexity(float)}
763
 */
764
  public float getConvexity()
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff