| 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()
 | 
 
transition the 'type' package