commit 649544b8fd81e84f935c1b5e4cec6f7b6dc8a291
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Fri Oct 21 02:18:53 2016 +0100

    Completely redesign Noise in the Dynamics and move all the complexity to the parent class.
    
    something does not work with it now :)

diff --git a/src/main/java/org/distorted/library/type/Dynamic.java b/src/main/java/org/distorted/library/type/Dynamic.java
index ed16adb..e7d3eb7 100644
--- a/src/main/java/org/distorted/library/type/Dynamic.java
+++ b/src/main/java/org/distorted/library/type/Dynamic.java
@@ -102,12 +102,60 @@ public abstract class Dynamic
 
   protected Vector<VectorNoise> vn;
   protected float[] mFactor;
+  protected float[][] baseV;
+  private float[] buffer;
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  // the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
+  // (tangent) is the vector tangent to the path.
+  // (cached) is the original vector from vv (copied here so when interpolating we can see if it is
+  // still valid and if not - rebuild the Cache
+
+  protected class VectorCache
+    {
+    float[] a;
+    float[] b;
+    float[] c;
+    float[] d;
+    float[] tangent;
+    float[] cached;
+
+    VectorCache(int dim)
+      {
+      a = new float[dim];
+      b = new float[dim];
+      c = new float[dim];
+      d = new float[dim];
+      tangent = new float[dim];
+      cached = new float[dim];
+      }
+    }
+
+  protected Vector<VectorCache> vc;
+  protected VectorCache tmp1, tmp2;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // hide this from Javadoc
   
-  Dynamic()
+  protected Dynamic()
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected Dynamic(int duration, float count, int dimension)
     {
+    vc = new Vector<>();
+    vn = null;
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = duration;
+    mCount = count;
+    mDimension = dimension;
+
+    baseV = new float[mDimension][mDimension];
+    buffer= new float[mDimension];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -194,6 +242,253 @@ public abstract class Dynamic
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// debugging only
+
+  private void printBase(String str)
+    {
+    String s;
+    float t;
+
+    for(int i=0; i<mDimension; i++)
+      {
+      s = "";
+
+      for(int j=0; j<mDimension; j++)
+        {
+        t = ((int)(1000*baseV[i][j]))/(1000.0f);
+        s+=(" "+t);
+        }
+      android.util.Log.e("dynamic", str+" base "+i+" : " + s);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// debugging only
+
+  private void checkBase()
+    {
+    float tmp;
+
+    for(int i=0; i<mDimension; i++)
+      for(int j=i+1; j<mDimension; j++)
+        {
+        tmp = 0.0f;
+
+        for(int k=0; k<mDimension; k++)
+          {
+          tmp += baseV[i][k]*baseV[j][k];
+          }
+
+        android.util.Log.e("dynamic", "vectors "+i+" and "+j+" : "+tmp);
+        }
+
+    for(int i=0; i<mDimension; i++)
+      {
+      tmp = 0.0f;
+
+      for(int k=0; k<mDimension; k++)
+        {
+        tmp += baseV[i][k]*baseV[i][k];
+        }
+
+      android.util.Log.e("dynamic", "length of vector "+i+" : "+Math.sqrt(tmp));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// helper function in case we are interpolating through exactly 2 points
+
+  protected void computeOrthonormalBase2(Static1D curr, Static1D next)
+    {
+    switch(mDimension)
+      {
+      case 1: baseV[0][0] = (next.x-curr.x);
+              break;
+      case 2: Static2D curr2 = (Static2D)curr;
+              Static2D next2 = (Static2D)next;
+              baseV[0][0] = (next2.x-curr2.x);
+              baseV[0][1] = (next2.y-curr2.y);
+              break;
+      case 3: Static3D curr3 = (Static3D)curr;
+              Static3D next3 = (Static3D)next;
+              baseV[0][0] = (next3.x-curr3.x);
+              baseV[0][1] = (next3.y-curr3.y);
+              baseV[0][2] = (next3.z-curr3.z);
+              break;
+      case 4: Static4D curr4 = (Static4D)curr;
+              Static4D next4 = (Static4D)next;
+              baseV[0][0] = (next4.x-curr4.x);
+              baseV[0][1] = (next4.y-curr4.y);
+              baseV[0][2] = (next4.z-curr4.z);
+              baseV[0][3] = (next4.w-curr4.w);
+              break;
+      case 5: Static5D curr5 = (Static5D)curr;
+              Static5D next5 = (Static5D)next;
+              baseV[0][0] = (next5.x-curr5.x);
+              baseV[0][1] = (next5.y-curr5.y);
+              baseV[0][2] = (next5.z-curr5.z);
+              baseV[0][3] = (next5.w-curr5.w);
+              baseV[0][4] = (next5.v-curr5.v);
+              break;
+      default: throw new RuntimeException("Unsupported dimension");
+      }
+
+    if( baseV[0][0] == 0.0f )
+      {
+      baseV[1][0] = 1.0f;
+      baseV[1][1] = 0.0f;
+      }
+    else
+      {
+      baseV[1][0] = 0.0f;
+      baseV[1][1] = 1.0f;
+      }
+
+    for(int i=2; i<mDimension; i++)
+      {
+      baseV[1][i] = 0.0f;
+      }
+
+    computeOrthonormalBase();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// helper function in case we are interpolating through more than 2 points
+
+  protected void computeOrthonormalBaseMore(float time,VectorCache vc)
+    {
+    for(int i=0; i<mDimension; i++)
+      {
+      baseV[0][i] = (3*vc.a[i]*time+2*vc.b[i])*time+vc.c[i];
+      baseV[1][i] =  6*vc.a[i]*time+2*vc.b[i];
+      }
+
+    computeOrthonormalBase();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// When this function gets called, baseV[0] and baseV[1] should have been filled with two mDimension-al
+// vectors. This function then fills the rest of the baseV array with a mDimension-al Orthonormal base.
+// (mDimension-2 vectors, pairwise orthogonal to each other and to the original 2). The function always
+// leaves base[0] alone but generally speaking must adjust base[1] to make it orthogonal to base[0]!
+// The whole baseV is then used to compute Noise.
+//
+// When computing noise of a point travelling along a N-dimensional path, there are three cases:
+// a) we may be interpolating through 1 point, i.e. standing in place - nothing to do in this case
+// b) we may be interpolating through 2 points, i.e. travelling along a straight line between them -
+//    then pass the velocity vector in baseV[0] and anything linearly independent in base[1].
+//    The output will then be discontinuous in dimensions>2 (sad corollary from the Hairy Ball Theorem)
+//    but we don't care - we are travelling along a straight line, so velocity (aka baseV[0]!) does
+//    not change.
+// c) we may be interpolating through more than 2 points. Then interpolation formulas ensure the path
+//    will never be a straight line, even locally -> we can pass in baseV[0] and baseV[1] the velocity
+//    and the acceleration (first and second derivatives of the path) which are then guaranteed to be
+//    linearly independent. Then we can ensure this is continuous in dimensions <=4. This leaves
+//    dimension 5 (ATM WAVE is 5-dimensional) discontinuous -> WAVE will suffer from chaotic noise.
+//
+// Bear in mind here the 'normal' in 'orthonormal' means 'length equal to the length of the original
+// velocity vector' (rather than the standard 1)
+
+  protected void computeOrthonormalBase()
+    {
+    int non_zeros=0;
+    int last_non_zero=-1;
+    float value;
+    for(int i=0; i<mDimension; i++)
+      {
+      value = baseV[0][i];
+
+      if( value != 0.0f )
+        {
+        non_zeros++;
+        last_non_zero=i;
+        }
+      }
+                         // velocity is the 0 vector -> two consecutive points we are interpolating
+    if( non_zeros==0 )   // through are identical -> no noise, set the base to 0 vectors.
+      {
+      for(int i=0; i<mDimension-1; i++)
+        for(int j=0; j<mDimension; j++)
+          baseV[i+1][j]= 0.0f;
+      }
+    else
+      {
+      for(int i=0; i<mDimension-1; i++)
+        for(int j=0; j<mDimension; j++)
+          {
+          if( (i<last_non_zero && j==i) || (i>=last_non_zero && j==i+1) )
+            baseV[i+1][j]= baseV[0][last_non_zero];
+          else
+            baseV[i+1][j]= 0.0f;
+          }
+
+      // That's it if velocity vector is already one of the standard orthonormal
+      // vectors. Otherwise (i.e. non_zeros>1) velocity is linearly independent
+      // to what's in baseV right now and we can use (modified!) Gram-Schmidt.
+      //
+      // b[0] = b[0]
+      // b[1] = b[1] - (<b[1],b[0]>/<b[0],b[0]>)*b[0]
+      // b[2] = b[2] - (<b[2],b[0]>/<b[0],b[0]>)*b[0] - (<b[2],b[1]>/<b[1],b[1]>)*b[1]
+      // 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]
+      //
+      // then b[i] = b[i] / |b[i]|
+
+      if( non_zeros>1 )
+        {
+        float tmp;
+
+        for(int i=1; i<mDimension; i++)
+          {
+          buffer[i-1]=0.0f;
+
+          for(int k=0; k<mDimension; k++)
+            {
+            value = baseV[i-1][k];
+            buffer[i-1] += value*value;
+            }
+
+          for(int j=0; j<i; j++)
+            {
+            tmp = 0.0f;
+
+            for(int k=0;k<mDimension; k++)
+              {
+              tmp += baseV[i][k]*baseV[j][k];
+              }
+
+            tmp /= buffer[j];
+
+            for(int k=0;k<mDimension; k++)
+              {
+              baseV[i][k] -= tmp*baseV[j][k];
+              }
+            }
+          }
+
+        buffer[mDimension-1]=0.0f;
+        for(int k=0; k<mDimension; k++)
+          {
+          value = baseV[mDimension-1][k];
+          buffer[mDimension-1] += value*value;
+          }
+
+        for(int i=1; i<mDimension; i++)
+          {
+          tmp = (float)Math.sqrt(buffer[0]/buffer[i]);
+
+          for(int k=0;k<mDimension; k++)
+            {
+            baseV[i][k] *= tmp;
+            }
+          }
+        }
+      }
+
+    //printBase("end");
+    //checkBase();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // internal debugging only!
   
diff --git a/src/main/java/org/distorted/library/type/Dynamic1D.java b/src/main/java/org/distorted/library/type/Dynamic1D.java
index f736325..396a570 100644
--- a/src/main/java/org/distorted/library/type/Dynamic1D.java
+++ b/src/main/java/org/distorted/library/type/Dynamic1D.java
@@ -29,24 +29,6 @@ import java.util.Vector;
 
 public class Dynamic1D extends Dynamic implements Data1D
   {
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the coefficients of the X(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
-// (x) is the vector tangent to the path.
-// (vx) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache
-   
-  private class VectorCache
-    {
-    float ax, bx, cx, dx;
-   
-    float x;
-    float vx;
-    }
-
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
- 
   private Vector<Static1D> vv;
   private Static1D prev, curr, next;
 
@@ -75,16 +57,16 @@ public class Dynamic1D extends Dynamic implements Data1D
       
       if( q>1 )
         {
-        tmp1.x = nx+px/q;
+        tmp1.tangent[0] = nx+px/q;
         }
       else
         {
-        tmp1.x = px+nx*q;
+        tmp1.tangent[0] = px+nx*q;
         }
       }
     else
       {
-      tmp1.x = 0.0f;
+      tmp1.tangent[0] = 0.0f;
       }
     }
       
@@ -97,10 +79,10 @@ public class Dynamic1D extends Dynamic implements Data1D
       tmp1= vc.elementAt(0);
       curr= vv.elementAt(0);
         
-      tmp1.ax = 0.0f;
-      tmp1.bx = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.dx = 0.0f;
+      tmp1.a[0] = 0.0f;
+      tmp1.b[0] = 0.0f;
+      tmp1.c[0] = curr.x;
+      tmp1.d[0] = 0.0f;
       }
     else if( numPoints==2 )
       {
@@ -109,15 +91,15 @@ public class Dynamic1D extends Dynamic implements Data1D
       curr= vv.elementAt(0);
       next= vv.elementAt(1);
           
-      tmp1.ax = 0.0f;
-      tmp1.bx = 0.0f;
-      tmp1.cx = next.x - curr.x;
-      tmp1.dx = curr.x;
+      tmp1.a[0] = 0.0f;
+      tmp1.b[0] = 0.0f;
+      tmp1.c[0] = next.x - curr.x;
+      tmp1.d[0] = curr.x;
       
-      tmp2.ax = 0.0f;
-      tmp2.bx = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.dx = next.x;
+      tmp2.a[0] = 0.0f;
+      tmp2.b[0] = 0.0f;
+      tmp2.c[0] = curr.x - next.x;
+      tmp2.d[0] = next.x;
       }
     else
       {
@@ -134,12 +116,12 @@ public class Dynamic1D extends Dynamic implements Data1D
         curr= vv.elementAt(i);
         next= vv.elementAt(n);
     
-        tmp1.vx = curr.x;
+        tmp1.cached[0] = curr.x;
         
-        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
-        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
-        tmp1.cx = tmp1.x;
-        tmp1.dx = curr.x;
+        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
+        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
+        tmp1.c[0] = tmp1.tangent[0];
+        tmp1.d[0] = curr.x;
         }
       }
    
@@ -154,7 +136,8 @@ public class Dynamic1D extends Dynamic implements Data1D
  */
   public Dynamic1D()
     {
-    this(0,0.5f);
+    super(0,0.5f,1);
+    vv = new Vector<>();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -169,15 +152,8 @@ public class Dynamic1D extends Dynamic implements Data1D
  */
   public Dynamic1D(int duration, float count)
     {
+    super(duration,count,1);
     vv = new Vector<>();
-    vc = new Vector<>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = duration;
-    mCount = count;
-    mDimension = 1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -237,12 +213,12 @@ public class Dynamic1D extends Dynamic implements Data1D
         {
         case 0: 
         case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(1));
+                vc.add(new VectorCache(1));
+                vc.add(new VectorCache(1));
                 cacheDirty = true;
                 break;
-        default:vc.add(new VectorCache());
+        default:vc.add(new VectorCache(1));
                 cacheDirty = true;
         }
      
@@ -269,12 +245,12 @@ public class Dynamic1D extends Dynamic implements Data1D
         {
         case 0:
         case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(1));
+                vc.add(new VectorCache(1));
+                vc.add(new VectorCache(1));
                 cacheDirty = true;
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCache(1));
                 cacheDirty = true;
         }
       
@@ -437,7 +413,7 @@ public class Dynamic1D extends Dynamic implements Data1D
                   next = vv.elementAt(vecNext);
                   tmp2 = vc.elementAt(vecNext);
               
-                  if( tmp2.vx!=next.x ) recomputeCache();
+                  if( tmp2.cached[0]!=next.x ) recomputeCache();
                   }
              
                 if( vn!=null )
@@ -446,7 +422,7 @@ public class Dynamic1D extends Dynamic implements Data1D
                   }
             
                 tmp1 = vc.elementAt(vecCurr);
-                buffer[offset] = ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
+                buffer[offset] = ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
                 break;
                 }
         }
diff --git a/src/main/java/org/distorted/library/type/Dynamic2D.java b/src/main/java/org/distorted/library/type/Dynamic2D.java
index bf30a6b..34beac4 100644
--- a/src/main/java/org/distorted/library/type/Dynamic2D.java
+++ b/src/main/java/org/distorted/library/type/Dynamic2D.java
@@ -29,25 +29,6 @@ import java.util.Vector;
 
 public class Dynamic2D extends Dynamic implements Data2D
   {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the coefficients of the X(t), Y(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
-// (x,y) is the vector tangent to the path.
-// (vx,vy) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache
-  
-  private class VectorCache
-    {
-    float ax, bx, cx, dx;
-    float ay, by, cy, dy;
-   
-    float x,y;
-    float vx,vy;
-    }
-
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-   
   private Vector<Static2D> vv;
   private Static2D prev, curr, next;
 
@@ -78,19 +59,19 @@ public class Dynamic2D extends Dynamic implements Data2D
       
       if( q>1 )
         {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
+        tmp1.tangent[0] = nx+px/q;
+        tmp1.tangent[1] = ny+py/q;
         }
       else
         {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
+        tmp1.tangent[0] = px+nx*q;
+        tmp1.tangent[1] = py+ny*q;
         }
       }
     else
       {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
+      tmp1.tangent[0] = 0.0f;
+      tmp1.tangent[1] = 0.0f;
       }
     }
    
@@ -103,11 +84,11 @@ public class Dynamic2D extends Dynamic implements Data2D
       tmp1= vc.elementAt(0);
       curr= vv.elementAt(0);
               
-      tmp1.ax = tmp1.ay = 0.0f;
-      tmp1.bx = tmp1.by = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.cy = curr.y;
-      tmp1.dx = tmp1.dy = 0.0f;
+      tmp1.a[0] = tmp1.a[1] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = 0.0f;
+      tmp1.c[0] = curr.x;
+      tmp1.c[1] = curr.y;
+      tmp1.d[0] = tmp1.d[1] = 0.0f;
       }
     else if( numPoints==2 )
       {
@@ -116,19 +97,19 @@ public class Dynamic2D extends Dynamic implements Data2D
       curr= vv.elementAt(0);
       next= vv.elementAt(1);
           
-      tmp1.ax = tmp1.ay = 0.0f;
-      tmp1.bx = tmp1.by = 0.0f;
-      tmp1.cx = next.x - curr.x;
-      tmp1.cy = next.y - curr.y;
-      tmp1.dx = curr.x;
-      tmp1.dy = curr.y;
+      tmp1.a[0] = tmp1.a[1] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = 0.0f;
+      tmp1.c[0] = next.x - curr.x;
+      tmp1.c[1] = next.y - curr.y;
+      tmp1.d[0] = curr.x;
+      tmp1.d[1] = curr.y;
       
-      tmp2.ax = tmp2.ay = 0.0f;
-      tmp2.bx = tmp2.by = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.cy = curr.y - next.y;
-      tmp2.dx = next.x;
-      tmp2.dy = next.y;
+      tmp2.a[0] = tmp2.a[1] = 0.0f;
+      tmp2.b[0] = tmp2.b[1] = 0.0f;
+      tmp2.c[0] = curr.x - next.x;
+      tmp2.c[1] = curr.y - next.y;
+      tmp2.d[0] = next.x;
+      tmp2.d[1] = next.y;
       }
     else
       {
@@ -145,18 +126,18 @@ public class Dynamic2D extends Dynamic implements Data2D
         curr= vv.elementAt(i);
         next= vv.elementAt(n);
       
-        tmp1.vx = curr.x;
-        tmp1.vy = curr.y;
-        
-        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
-        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
-        tmp1.cx = tmp1.x;
-        tmp1.dx = curr.x;
-      
-        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
-        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
-        tmp1.cy = tmp1.y;
-        tmp1.dy = curr.y;
+        tmp1.cached[0] = curr.x;
+        tmp1.cached[1] = curr.y;
+
+        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
+        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
+        tmp1.c[0] = tmp1.tangent[0];
+        tmp1.d[0] = curr.x;
+
+        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
+        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
+        tmp1.c[1] = tmp1.tangent[1];
+        tmp1.d[1] = curr.y;
         }
       }
     
@@ -171,7 +152,8 @@ public class Dynamic2D extends Dynamic implements Data2D
  */
   public Dynamic2D()
     {
-    this(0,0.5f);
+    super(0,0.5f,2);
+    vv = new Vector<>();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -186,15 +168,8 @@ public class Dynamic2D extends Dynamic implements Data2D
  */
   public Dynamic2D(int duration, float count)
     {
+    super(duration,count,2);
     vv = new Vector<>();
-    vc = new Vector<>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = duration;
-    mCount = count;
-    mDimension = 2;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -254,11 +229,11 @@ public class Dynamic2D extends Dynamic implements Data2D
         {
         case 0:
         case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(2));
+                vc.add(new VectorCache(2));
+                vc.add(new VectorCache(2));
                 break;
-        default:vc.add(new VectorCache());
+        default:vc.add(new VectorCache(2));
         }
      
       numPoints++;
@@ -285,11 +260,11 @@ public class Dynamic2D extends Dynamic implements Data2D
         {
         case 0:
         case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(2));
+                vc.add(new VectorCache(2));
+                vc.add(new VectorCache(2));
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCache(2));
         }
       
       numPoints++;
@@ -417,11 +392,11 @@ public class Dynamic2D extends Dynamic implements Data2D
                 {
                 time = noise(time,0);
               
-                float dx2 = next.x-curr.x;
-                float dy2 = next.y-curr.y;
+                baseV[0][0] = next.x-curr.x;
+                baseV[0][1] = next.y-curr.y;
    
-                buffer[offset  ] = dx2*time + curr.x +dy2*mFactor[0];
-                buffer[offset+1] = dy2*time + curr.y -dx2*mFactor[0];
+                buffer[offset  ] = baseV[0][0]*time + curr.x +baseV[0][0]*mFactor[0];
+                buffer[offset+1] = baseV[0][1]*time + curr.y -baseV[0][1]*mFactor[0];
                 }
               else
                 {
@@ -468,25 +443,25 @@ public class Dynamic2D extends Dynamic implements Data2D
                   next = vv.elementAt(vecNext);
                   tmp2 = vc.elementAt(vecNext);
               
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y ) recomputeCache();
+                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y ) recomputeCache();
                   }
-              
+
+                tmp1 = vc.elementAt(vecCurr);
+
                 if( vn!=null )
                   {
                   time = noise(time,vecCurr);
-                  tmp1 = vc.elementAt(vecCurr);
-               
-                  float dx2 = (3*tmp1.ax*time+2*tmp1.bx)*time + tmp1.cx;
-                  float dy2 = (3*tmp1.ay*time+2*tmp1.by)*time + tmp1.cy;
+
+                  baseV[0][0] = (3*tmp1.a[0]*time+2*tmp1.b[0])*time + tmp1.c[0];
+                  baseV[0][1] = (3*tmp1.a[1]*time+2*tmp1.b[1])*time + tmp1.c[1];
                  
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx +dy2*mFactor[0];
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy -dx2*mFactor[0];
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] +baseV[0][0]*mFactor[0];
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] -baseV[0][1]*mFactor[0];
                   } 
                 else
                   {
-                  tmp1 = vc.elementAt(vecCurr);
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
                   }
                 
                 break;
diff --git a/src/main/java/org/distorted/library/type/Dynamic3D.java b/src/main/java/org/distorted/library/type/Dynamic3D.java
index 82ab69b..ab82f80 100644
--- a/src/main/java/org/distorted/library/type/Dynamic3D.java
+++ b/src/main/java/org/distorted/library/type/Dynamic3D.java
@@ -29,32 +29,9 @@ import java.util.Vector;
 
 public class Dynamic3D extends Dynamic implements Data3D
   {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
-// (x,y,z) is the vector tangent to the path.
-// (vx,vy,vz) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache
-  
-  private class VectorCache
-    {
-    float ax, bx, cx, dx;
-    float ay, by, cy, dy;
-    float az, bz, cz, dz;
-   
-    float x,y,z;
-    float vx,vy,vz;
-    }
-
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
   private Vector<Static3D> vv;
   private Static3D prev, curr, next;
 
-  private float vec1X,vec1Y,vec1Z;   // vector perpendicular to v(t) and in the same plane as v(t) and a(t) (for >2 points only, in case of 2 points this is calculated differently)
-  private float vec2X,vec2Y,vec2Z;   // vector perpendicular to v(t0 and to vec1.
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // no array bounds checking!
   
@@ -84,22 +61,22 @@ public class Dynamic3D extends Dynamic implements Data3D
       
       if( q>1 )
         {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        tmp1.z = nz+pz/q;
+        tmp1.tangent[0] = nx+px/q;
+        tmp1.tangent[1] = ny+py/q;
+        tmp1.tangent[2] = nz+pz/q;
         }
       else
         {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        tmp1.z = pz+nz*q;
+        tmp1.tangent[0] = px+nx*q;
+        tmp1.tangent[1] = py+ny*q;
+        tmp1.tangent[2] = pz+nz*q;
         }
       }
     else
       {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      tmp1.z = 0.0f;  
+      tmp1.tangent[0] = 0.0f;
+      tmp1.tangent[1] = 0.0f;
+      tmp1.tangent[2] = 0.0f;
       }
     }
     
@@ -112,12 +89,12 @@ public class Dynamic3D extends Dynamic implements Data3D
       tmp1= vc.elementAt(0);
       curr= vv.elementAt(0);
         
-      tmp1.ax = tmp1.ay = tmp1.az = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.cy = curr.y;
-      tmp1.cz = curr.z;
-      tmp1.dx = tmp1.dy = tmp1.dz = 0.0f;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = 0.0f;
+      tmp1.c[0] = curr.x;
+      tmp1.c[1] = curr.y;
+      tmp1.c[2] = curr.z;
+      tmp1.d[0] = tmp1.d[1] = tmp1.d[2] = 0.0f;
       }
     else if( numPoints==2 )
       {
@@ -126,23 +103,23 @@ public class Dynamic3D extends Dynamic implements Data3D
       curr= vv.elementAt(0);
       next= vv.elementAt(1);
           
-      tmp1.ax = tmp1.ay = tmp1.az = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = 0.0f;
-      tmp1.cx = next.x - curr.x;
-      tmp1.cy = next.y - curr.y;
-      tmp1.cz = next.z - curr.z;
-      tmp1.dx = curr.x;
-      tmp1.dy = curr.y;
-      tmp1.dz = curr.z;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = 0.0f;
+      tmp1.c[0] = next.x - curr.x;
+      tmp1.c[1] = next.y - curr.y;
+      tmp1.c[2] = next.z - curr.z;
+      tmp1.d[0] = curr.x;
+      tmp1.d[1] = curr.y;
+      tmp1.d[2] = curr.z;
       
-      tmp2.ax = tmp2.ay = tmp2.az = 0.0f;
-      tmp2.bx = tmp2.by = tmp2.bz = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.cy = curr.y - next.y;
-      tmp2.cz = curr.z - next.z;
-      tmp2.dx = next.x;
-      tmp2.dy = next.y;
-      tmp2.dz = next.z;
+      tmp2.a[0] = tmp2.a[1] = tmp2.a[2] = 0.0f;
+      tmp2.b[0] = tmp2.b[1] = tmp2.b[2] = 0.0f;
+      tmp2.c[0] = curr.x - next.x;
+      tmp2.c[1] = curr.y - next.y;
+      tmp2.c[2] = curr.z - next.z;
+      tmp2.d[0] = next.x;
+      tmp2.d[1] = next.y;
+      tmp2.d[2] = next.z;
       }
     else
       {
@@ -159,112 +136,30 @@ public class Dynamic3D extends Dynamic implements Data3D
         curr= vv.elementAt(i);
         next= vv.elementAt(n);
       
-        tmp1.vx = curr.x;
-        tmp1.vy = curr.y;
-        tmp1.vz = curr.z;
+        tmp1.cached[0] = curr.x;
+        tmp1.cached[1] = curr.y;
+        tmp1.cached[2] = curr.z;
         
-        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
-        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
-        tmp1.cx = tmp1.x;
-        tmp1.dx = curr.x;
+        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
+        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
+        tmp1.c[0] = tmp1.tangent[0];
+        tmp1.d[0] = curr.x;
       
-        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
-        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
-        tmp1.cy = tmp1.y;
-        tmp1.dy = curr.y;
+        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
+        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
+        tmp1.c[1] = tmp1.tangent[1];
+        tmp1.d[1] = curr.y;
       
-        tmp1.az =  2*curr.z +   tmp1.z - 2*next.z + tmp2.z;
-        tmp1.bz = -3*curr.z - 2*tmp1.z + 3*next.z - tmp2.z;
-        tmp1.cz = tmp1.z;
-        tmp1.dz = curr.z;
+        tmp1.a[2] =  2*curr.z +   tmp1.tangent[2] - 2*next.z + tmp2.tangent[2];
+        tmp1.b[2] = -3*curr.z - 2*tmp1.tangent[2] + 3*next.z - tmp2.tangent[2];
+        tmp1.c[2] = tmp1.tangent[2];
+        tmp1.d[2] = curr.z;
         }
       }
    
     cacheDirty = false;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// v is the speed vector (i.e. position p(t) differentiated by time)
-// a is the acceleration vector (differentiate once more)
-// now what we are doing is compute vec1{X,Y,Z} to be a vector perpendicular to v and in the same plane as both v and a.
-// vec2{X,Y,Z} would be (v)x(vec1).
-//  
-// vec1 = a-delta*v where delta = (v*a)/|v|^2   (see Gram-Schmidt)
-  
-  private void setUpVectors(float time,VectorCache vc)
-    {
-    if( vc!=null )
-      {
-      float vx = (3*vc.ax*time+2*vc.bx)*time+vc.cx;
-      float vy = (3*vc.ay*time+2*vc.by)*time+vc.cy;
-      float vz = (3*vc.az*time+2*vc.bz)*time+vc.cz;
-     
-      float ax = 6*vc.ax*time+2*vc.bx;
-      float ay = 6*vc.ay*time+2*vc.by;
-      float az = 6*vc.az*time+2*vc.bz;
-     
-      float v_sq = vx*vx+vy*vy+vz*vz;
-      float delta = (vx*ax+vy*ay+vz*az)/v_sq;
-     
-      vec1X = ax-delta*vx;
-      vec1Y = ay-delta*vy;
-      vec1Z = az-delta*vz;
-     
-      vec2X = vy*vec1Z-vz*vec1Y;
-      vec2Y = vz*vec1X-vx*vec1Z;
-      vec2Z = vx*vec1Y-vy*vec1X;
-     
-      float len1 = (float)Math.sqrt(v_sq/(vec1X*vec1X+vec1Y*vec1Y+vec1Z*vec1Z));
-      float len2 = (float)Math.sqrt(v_sq/(vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z));   
-     
-      vec1X*=len1;
-      vec1Y*=len1;
-      vec1Z*=len1;
-     
-      vec2X*=len2;
-      vec2Y*=len2;
-      vec2Z*=len2;
-      }
-    else
-      {
-      curr = vv.elementAt(0);
-      next = vv.elementAt(1); 
-     
-      float vx = (next.x-curr.x);
-      float vy = (next.y-curr.y);
-      float vz = (next.z-curr.z);
-     
-      float b = (float)Math.sqrt(vx*vx+vy*vy);
-     
-      if( b>0.0f )
-        {
-        vec1X = vx*vz/b;
-        vec1Y = vy*vz/b;
-        vec1Z = -b;
-      
-        vec2X = vy*vec1Z-vz*vec1Y;
-        vec2Y = vz*vec1X-vx*vec1Z;
-        vec2Z = vx*vec1Y-vy*vec1X;
-       
-        float len2 = (float)Math.sqrt((vx*vx+vy*vy+vz*vz)/(vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z));
-       
-        vec2X*=len2;
-        vec2Y*=len2;
-        vec2Z*=len2;
-        }
-      else
-        {
-        vec1X = vz;
-        vec1Y = 0.0f;
-        vec1Z = 0.0f;
-      
-        vec2X = 0.0f;
-        vec2Y = vz;
-        vec2Z = 0.0f;
-        }
-      }
-    }
-  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -273,7 +168,8 @@ public class Dynamic3D extends Dynamic implements Data3D
  */
   public Dynamic3D()
     {
-    this(0,0.5f);
+    super(0,0.5f,3);
+    vv = new Vector<>();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -288,15 +184,8 @@ public class Dynamic3D extends Dynamic implements Data3D
  */
   public Dynamic3D(int duration, float count)
     {
+    super(duration,count,3);
     vv = new Vector<>();
-    vc = new Vector<>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = duration;
-    mCount = count;
-    mDimension = 3;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -355,13 +244,13 @@ public class Dynamic3D extends Dynamic implements Data3D
       switch(numPoints)
         {
         case 0: break;
-        case 1: setUpVectors(0.0f,null);
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                 break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(3));
+                vc.add(new VectorCache(3));
+                vc.add(new VectorCache(3));
                 break;
-        default:vc.add(new VectorCache());
+        default:vc.add(new VectorCache(3));
         }
 
       numPoints++;
@@ -387,13 +276,13 @@ public class Dynamic3D extends Dynamic implements Data3D
       switch(numPoints)
         {
         case 0: break;
-        case 1: setUpVectors(0.0f,null);
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                 break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(3));
+                vc.add(new VectorCache(3));
+                vc.add(new VectorCache(3));
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCache(3));
         }
 
       numPoints++;
@@ -425,7 +314,7 @@ public class Dynamic3D extends Dynamic implements Data3D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.remove(n);
         }
@@ -464,7 +353,7 @@ public class Dynamic3D extends Dynamic implements Data3D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.removeElementAt(location);
         }
@@ -525,9 +414,9 @@ public class Dynamic3D extends Dynamic implements Data3D
                 {
                 time = noise(time,0);
             
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor[0] + vec2X*mFactor[1]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor[0] + vec2Y*mFactor[1]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor[0] + vec2Z*mFactor[1]);
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1]);
                 }
               else
                 {
@@ -575,7 +464,7 @@ public class Dynamic3D extends Dynamic implements Data3D
                   next = vv.elementAt(vecNext);
                   tmp2 = vc.elementAt(vecNext);
               
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z ) recomputeCache();
+                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z ) recomputeCache();
                   }
             
                 tmp1 = vc.elementAt(vecCurr);
@@ -584,17 +473,17 @@ public class Dynamic3D extends Dynamic implements Data3D
                   {
                   time = noise(time,vecCurr);
               
-                  setUpVectors(time,tmp1);
+                  computeOrthonormalBaseMore(time,tmp1);
                  
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor[0] + vec2X*mFactor[1]);
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor[0] + vec2Y*mFactor[1]);
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor[0] + vec2Z*mFactor[1]);
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1]);
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1]);
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2] + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1]);
                   }
                 else
                   {
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz;
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
                   }
                
                 break;
diff --git a/src/main/java/org/distorted/library/type/Dynamic4D.java b/src/main/java/org/distorted/library/type/Dynamic4D.java
index 50b73cf..8e13ae3 100644
--- a/src/main/java/org/distorted/library/type/Dynamic4D.java
+++ b/src/main/java/org/distorted/library/type/Dynamic4D.java
@@ -29,34 +29,9 @@ import java.util.Vector;
 
 public class Dynamic4D extends Dynamic implements Data4D
   {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the coefficients of the X(t), Y(t), Z(t), W(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
-// (x,y,z,w) is the vector tangent to the path.
-// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache
-  
-  private class VectorCache
-    {
-    float ax, bx, cx, dx;
-    float ay, by, cy, dy;
-    float az, bz, cz, dz;
-    float aw, bw, cw, dw;
-   
-    float x,y,z,w;
-    float vx,vy,vz,vw;
-    }
-
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
   private Vector<Static4D> vv;
   private Static4D prev, curr, next;
 
-  private float vec1X,vec1Y,vec1Z,vec1W;      // vector perpendicular to v(t) and in the same plane as v(t) and a(t) (for >2 points only, in case of 2 points this is calculated differently)
-  private float vec2X,vec2Y,vec2Z,vec2W;      // vector perpendicular to v(t) and to vec1.
-  private float vec3X,vec3Y,vec3Z,vec3W;      // vector perpendicular to v(t) and to vec1.
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // no array bounds checking!
   
@@ -88,25 +63,25 @@ public class Dynamic4D extends Dynamic implements Data4D
       
       if( q>1 )
         {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        tmp1.z = nz+pz/q;
-        tmp1.w = nw+pw/q;
+        tmp1.tangent[0] = nx+px/q;
+        tmp1.tangent[1] = ny+py/q;
+        tmp1.tangent[2] = nz+pz/q;
+        tmp1.tangent[3] = nw+pw/q;
         }
       else
         {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        tmp1.z = pz+nz*q;
-        tmp1.w = pw+nw*q;
+        tmp1.tangent[0] = px+nx*q;
+        tmp1.tangent[1] = py+ny*q;
+        tmp1.tangent[2] = pz+nz*q;
+        tmp1.tangent[3] = pw+nw*q;
         }
       }
     else
       {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      tmp1.z = 0.0f;  
-      tmp1.w = 0.0f;
+      tmp1.tangent[0] = 0.0f;
+      tmp1.tangent[1] = 0.0f;
+      tmp1.tangent[2] = 0.0f;
+      tmp1.tangent[3] = 0.0f;
       }
     }
     
@@ -119,13 +94,13 @@ public class Dynamic4D extends Dynamic implements Data4D
       tmp1= vc.elementAt(0);
       curr= vv.elementAt(0);
         
-      tmp1.ax = tmp1.ay = tmp1.az = tmp1.aw = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = tmp1.bw = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.cy = curr.y;
-      tmp1.cz = curr.z;
-      tmp1.cw = curr.w;
-      tmp1.dx = tmp1.dy = tmp1.dz = tmp1.dw = 0.0f;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = tmp1.a[3] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = tmp1.b[3] = 0.0f;
+      tmp1.c[0] = curr.x;
+      tmp1.c[1] = curr.y;
+      tmp1.c[2] = curr.z;
+      tmp1.c[3] = curr.w;
+      tmp1.d[0] = tmp1.d[1] = tmp1.d[3] = tmp1.d[3] = 0.0f;
       }
     else if( numPoints==2 )
       {
@@ -134,27 +109,27 @@ public class Dynamic4D extends Dynamic implements Data4D
       curr= vv.elementAt(0);
       next= vv.elementAt(1);
       
-      tmp1.ax = tmp1.ay = tmp1.az = tmp1.aw = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = tmp1.bw = 0.0f;
-      tmp1.cx = next.x - curr.x;
-      tmp1.cy = next.y - curr.y;
-      tmp1.cz = next.z - curr.z;
-      tmp1.cw = next.w - curr.w;
-      tmp1.dx = curr.x;
-      tmp1.dy = curr.y;
-      tmp1.dz = curr.z;
-      tmp1.dw = curr.w;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = tmp1.a[3] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = tmp1.b[3] = 0.0f;
+      tmp1.c[0] = next.x - curr.x;
+      tmp1.c[1] = next.y - curr.y;
+      tmp1.c[2] = next.z - curr.z;
+      tmp1.c[3] = next.w - curr.w;
+      tmp1.d[0] = curr.x;
+      tmp1.d[1] = curr.y;
+      tmp1.d[2] = curr.z;
+      tmp1.d[3] = curr.w;
       
-      tmp2.ax = tmp2.ay = tmp2.az = tmp2.aw = 0.0f;
-      tmp2.bx = tmp2.by = tmp2.bz = tmp2.bw = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.cy = curr.y - next.y;
-      tmp2.cz = curr.z - next.z;
-      tmp2.cw = curr.w - next.w;
-      tmp2.dx = next.x;
-      tmp2.dy = next.y;
-      tmp2.dz = next.z;
-      tmp2.dw = next.w;
+      tmp2.a[0] = tmp2.a[1] = tmp2.a[2] = tmp2.a[3] = 0.0f;
+      tmp2.b[0] = tmp2.b[1] = tmp2.b[2] = tmp2.b[3] = 0.0f;
+      tmp2.c[0] = curr.x - next.x;
+      tmp2.c[1] = curr.y - next.y;
+      tmp2.c[2] = curr.z - next.z;
+      tmp2.c[3] = curr.w - next.w;
+      tmp2.d[0] = next.x;
+      tmp2.d[1] = next.y;
+      tmp2.d[2] = next.z;
+      tmp2.d[3] = next.w;
       }
     else
       {
@@ -171,187 +146,36 @@ public class Dynamic4D extends Dynamic implements Data4D
         curr= vv.elementAt(i);
         next= vv.elementAt(n);
       
-        tmp1.vx = curr.x;
-        tmp1.vy = curr.y;
-        tmp1.vz = curr.z;
-        tmp1.vw = curr.w;
+        tmp1.cached[0] = curr.x;
+        tmp1.cached[1] = curr.y;
+        tmp1.cached[2] = curr.z;
+        tmp1.cached[3] = curr.w;
         
-        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
-        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
-        tmp1.cx = tmp1.x;
-        tmp1.dx = curr.x;
+        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
+        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
+        tmp1.c[0] = tmp1.tangent[0];
+        tmp1.d[0] = curr.x;
       
-        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
-        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
-        tmp1.cy = tmp1.y;
-        tmp1.dy = curr.y;
+        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
+        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
+        tmp1.c[1] = tmp1.tangent[1];
+        tmp1.d[1] = curr.y;
       
-        tmp1.az =  2*curr.z +   tmp1.z - 2*next.z + tmp2.z;
-        tmp1.bz = -3*curr.z - 2*tmp1.z + 3*next.z - tmp2.z;
-        tmp1.cz = tmp1.z;
-        tmp1.dz = curr.z;
+        tmp1.a[2] =  2*curr.z +   tmp1.tangent[2] - 2*next.z + tmp2.tangent[2];
+        tmp1.b[2] = -3*curr.z - 2*tmp1.tangent[2] + 3*next.z - tmp2.tangent[2];
+        tmp1.c[2] = tmp1.tangent[2];
+        tmp1.d[2] = curr.z;
         
-        tmp1.aw =  2*curr.w +   tmp1.w - 2*next.w + tmp2.w;
-        tmp1.bw = -3*curr.w - 2*tmp1.w + 3*next.w - tmp2.w;
-        tmp1.cw = tmp1.w;
-        tmp1.dw = curr.w;
+        tmp1.a[3] =  2*curr.w +   tmp1.tangent[3] - 2*next.w + tmp2.tangent[3];
+        tmp1.b[3] = -3*curr.w - 2*tmp1.tangent[3] + 3*next.w - tmp2.tangent[3];
+        tmp1.c[3] = tmp1.tangent[3];
+        tmp1.d[3] = curr.w;
         }
       }
    
     cacheDirty = false;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// v is the speed vector (i.e. position p(t) differentiated by time)
-// a is the acceleration vector (differentiate once more)
-//
-// Now we construct orthogonal basis with Gram-Schmidt:  
-// vec1 = a-delta*v where delta = (v*a)/|v|^2
-// vec2 = (0,0,1,0) - coeff1*(vx,vy,vz,vw) - coeff2*(vec1x,vec1y,vec1z,vec1w)                                     where coeff1 = vz/|v|^2, coeff2 = vec1Z/|vec1|^2
-// vec3 = (0,0,0,1) - coeff1*(vx,vy,vz,vw) - coeff2*(vec1x,vec1y,vec1z,vec1w) - coeff3*(vec2x,vec2y,vec2z,vec2w)  where coeff1 = vw/|v|^2, coeff2 = vec1W/|vec1|^2, coeff3 = vec2W/|vec2|^2
-    
-  private void setUpVectors(float time,VectorCache vc)
-    {
-    if( vc!=null )
-      {
-      float vx = (3*vc.ax*time+2*vc.bx)*time+vc.cx;
-      float vy = (3*vc.ay*time+2*vc.by)*time+vc.cy;
-      float vz = (3*vc.az*time+2*vc.bz)*time+vc.cz;
-      float vw = (3*vc.aw*time+2*vc.bw)*time+vc.cw;
-     
-      float ax = 6*vc.ax*time+2*vc.bx;
-      float ay = 6*vc.ay*time+2*vc.by;
-      float az = 6*vc.az*time+2*vc.bz;
-      float aw = 6*vc.aw*time+2*vc.bw;
-     
-      float v_sq = vx*vx+vy*vy+vz*vz+vw*vw;
-      float delta = (vx*ax+vy*ay+vz*az+vw*aw)/v_sq;
-     
-      vec1X = ax-delta*vx;
-      vec1Y = ay-delta*vy;
-      vec1Z = az-delta*vz;
-      vec1W = aw-delta*vw;
-     
-      float vec1_sq = vec1X*vec1X+vec1Y*vec1Y+vec1Z*vec1Z+vec1W*vec1W;
-     
-      // construct vec2 and vec3. Cross product does not work in 4th dimension!
-      float coeff21 = vz/v_sq;
-      float coeff22 = vec1Z/vec1_sq;
-      vec2X = 0.0f - coeff21*vx - coeff22*vec1X;
-      vec2Y = 0.0f - coeff21*vy - coeff22*vec1Y;
-      vec2Z = 1.0f - coeff21*vz - coeff22*vec1Z;
-      vec2W = 0.0f - coeff21*vw - coeff22*vec1W;
-     
-      float vec2_sq = vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z+vec2W*vec2W;
-      float coeff31 = vw/v_sq;
-      float coeff32 = vec1W/vec1_sq;
-      float coeff33 = vec2W/vec2_sq;
-      vec3X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-      vec3Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-      vec3Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-      vec3W = 1.0f - coeff31*vw - coeff32*vec1W - coeff33*vec2W;
-     
-      float vec3_sq = vec3X*vec3X+vec3Y*vec3Y+vec3Z*vec3Z+vec3W*vec3W;
-     
-      float len1 = (float)Math.sqrt(v_sq/vec1_sq);   
-      float len2 = (float)Math.sqrt(v_sq/vec2_sq);   
-      float len3 = (float)Math.sqrt(v_sq/vec3_sq);
-     
-      vec1X*=len1;
-      vec1Y*=len1;
-      vec1Z*=len1;
-      vec1W*=len1;
-     
-      vec2X*=len2;
-      vec2Y*=len2;
-      vec2Z*=len2;
-      vec2W*=len2;
-     
-      vec3X*=len3;
-      vec3Y*=len3;
-      vec3Z*=len3;
-      vec3W*=len3;
-      }
-    else
-      {
-      curr = vv.elementAt(0);
-      next = vv.elementAt(1); 
-     
-      float vx = (next.x-curr.x);
-      float vy = (next.y-curr.y);
-      float vz = (next.z-curr.z);
-      float vw = (next.w-curr.w);
-     
-      float b = (float)Math.sqrt(vx*vx+vy*vy+vz*vz);
-     
-      if( b>0.0f )
-        {
-        vec1X = vx*vw/b;
-        vec1Y = vy*vw/b;
-        vec1Z = vz*vw/b;
-        vec1W = -b;
-      
-        float v_sq = vx*vx+vy*vy+vz*vz+vw*vw;
-        float vec1_sq = vec1X*vec1X+vec1Y*vec1Y+vec1Z*vec1Z+vec1W*vec1W;
-     
-        // construct vec2 and vec3. Cross product does not work in 4th dimension!
-        float coeff21 = vz/v_sq;
-        float coeff22 = vec1Z/vec1_sq;
-        vec2X = 0.0f - coeff21*vx - coeff22*vec1X;
-        vec2Y = 0.0f - coeff21*vy - coeff22*vec1Y;
-        vec2Z = 1.0f - coeff21*vz - coeff22*vec1Z;
-        vec2W = 0.0f - coeff21*vw - coeff22*vec1W;
-     
-        float vec2_sq = vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z+vec2W*vec2W;
-        float coeff31 = vw/v_sq;
-        float coeff32 = vec1W/vec1_sq;
-        float coeff33 = vec2W/vec2_sq;
-        vec3X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-        vec3Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-        vec3Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-        vec3W = 1.0f - coeff31*vw - coeff32*vec1W - coeff33*vec2W;
-     
-        float vec3_sq = vec3X*vec3X+vec3Y*vec3Y+vec3Z*vec3Z+vec3W*vec3W;
-     
-        float len1 = (float)Math.sqrt(v_sq/vec1_sq);    
-        float len2 = (float)Math.sqrt(v_sq/vec2_sq);    
-        float len3 = (float)Math.sqrt(v_sq/vec3_sq);
-     
-        vec1X*=len1;
-        vec1Y*=len1;
-        vec1Z*=len1;
-        vec1W*=len1;
-     
-        vec2X*=len2;
-        vec2Y*=len2;
-        vec2Z*=len2;
-        vec2W*=len2;
-     
-        vec3X*=len3;
-        vec3Y*=len3;
-        vec3Z*=len3;
-        vec3W*=len3;
-        }
-      else
-        {
-        vec1X = vw;
-        vec1Y = 0.0f;
-        vec1Z = 0.0f;
-        vec1W = 0.0f;
-      
-        vec2X = 0.0f;
-        vec2Y = vw;
-        vec2Z = 0.0f;
-        vec2W = 0.0f;
-      
-        vec3X = 0.0f;
-        vec3Y = 0.0f;
-        vec3Z = vw;
-        vec3W = 0.0f;
-        }
-      }
-    }
-  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -360,7 +184,8 @@ public class Dynamic4D extends Dynamic implements Data4D
  */
   public Dynamic4D()
     {
-    this(0,0.5f);
+    super(0,0.5f,4);
+    vv = new Vector<>();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -375,15 +200,8 @@ public class Dynamic4D extends Dynamic implements Data4D
  */
   public Dynamic4D(int duration, float count)
     {
+    super(duration,count,4);
     vv = new Vector<>();
-    vc = new Vector<>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = duration;
-    mCount = count;
-    mDimension = 4;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -442,13 +260,13 @@ public class Dynamic4D extends Dynamic implements Data4D
        switch(numPoints)
          {
          case 0: break;
-         case 1: setUpVectors(0.0f,null);
+         case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                  break;
-         case 2: vc.add(new VectorCache());
-                 vc.add(new VectorCache());
-                 vc.add(new VectorCache());
+         case 2: vc.add(new VectorCache(4));
+                 vc.add(new VectorCache(4));
+                 vc.add(new VectorCache(4));
                  break;
-         default:vc.add(new VectorCache());
+         default:vc.add(new VectorCache(4));
          }
 
        numPoints++;
@@ -474,13 +292,13 @@ public class Dynamic4D extends Dynamic implements Data4D
       switch(numPoints)
         {
         case 0: break;
-        case 1: setUpVectors(0.0f,null);
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                 break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(4));
+                vc.add(new VectorCache(4));
+                vc.add(new VectorCache(4));
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCache(4));
         }
 
       numPoints++;
@@ -512,7 +330,7 @@ public class Dynamic4D extends Dynamic implements Data4D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.remove(n);
         }
@@ -551,7 +369,7 @@ public class Dynamic4D extends Dynamic implements Data4D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.removeElementAt(location);
         }
@@ -613,11 +431,11 @@ public class Dynamic4D extends Dynamic implements Data4D
               if( vn!=null )
                 {
                 time = noise(time,0);
-            
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor[0] + vec2X*mFactor[1] + vec3X*mFactor[2]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor[0] + vec2Y*mFactor[1] + vec3Y*mFactor[2]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor[0] + vec2Z*mFactor[1] + vec3Z*mFactor[2]);
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (vec1W*mFactor[0] + vec2W*mFactor[1] + vec3W*mFactor[2]);
+
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1] + baseV[2][0]*mFactor[2]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1] + baseV[2][1]*mFactor[2]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1] + baseV[2][2]*mFactor[2]);
+                buffer[offset+3] = (next.z-curr.z)*time + curr.z + (baseV[0][3]*mFactor[0] + baseV[1][3]*mFactor[1] + baseV[2][3]*mFactor[2]);
                 }
               else
                 {
@@ -666,7 +484,7 @@ public class Dynamic4D extends Dynamic implements Data4D
                   next = vv.elementAt(vecNext);
                   tmp2 = vc.elementAt(vecNext);
               
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
+                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z || tmp2.cached[3]!=next.w ) recomputeCache();
                   }
             
                 tmp1 = vc.elementAt(vecCurr);
@@ -675,19 +493,19 @@ public class Dynamic4D extends Dynamic implements Data4D
                   {
                   time = noise(time,vecCurr);
               
-                  setUpVectors(time,tmp1);
-                 
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor[0] + vec2X*mFactor[1] + vec3X*mFactor[2]);
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor[0] + vec2Y*mFactor[1] + vec3Y*mFactor[2]);
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor[0] + vec2Z*mFactor[1] + vec3Z*mFactor[2]);
-                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw + (vec1W*mFactor[0] + vec2W*mFactor[1] + vec3W*mFactor[2]);
+                  computeOrthonormalBaseMore(time,tmp1);
+
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1] + baseV[2][0]*mFactor[2]);
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1] + baseV[2][1]*mFactor[2]);
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2] + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1] + baseV[2][2]*mFactor[2]);
+                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3] + (baseV[0][3]*mFactor[0] + baseV[1][3]*mFactor[1] + baseV[2][3]*mFactor[2]);
                   }
                 else
                   {
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz;
-                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw;
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
+                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3];
                   }
  
                 break;
diff --git a/src/main/java/org/distorted/library/type/Dynamic5D.java b/src/main/java/org/distorted/library/type/Dynamic5D.java
index c47216b..1c18b82 100644
--- a/src/main/java/org/distorted/library/type/Dynamic5D.java
+++ b/src/main/java/org/distorted/library/type/Dynamic5D.java
@@ -29,36 +29,9 @@ import java.util.Vector;
 
 public class Dynamic5D extends Dynamic implements Data5D
   {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the coefficients of the X(t), Y(t), Z(t), W(t), V(t) polynomials: X(t) = ax*T^3 + bx*T^2 + cx*t + dx  etc.
-// (x,y,z,w,v) is the vector tangent to the path.
-// (vx,vy,vz,vw,vv) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache
-  
-  private class VectorCache
-    {
-    float ax, bx, cx, dx;
-    float ay, by, cy, dy;
-    float az, bz, cz, dz;
-    float aw, bw, cw, dw;
-    float av, bv, cv, dv;
-   
-    float x,y,z,w,v;
-    float vx,vy,vz,vw,vv;
-    }
-
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
   private Vector<Static5D> vv;
   private Static5D prev, curr, next;
 
-  private float vec1X,vec1Y,vec1Z,vec1W,vec1V; //
-  private float vec2X,vec2Y,vec2Z,vec2W,vec2V; // 4 base noise vectors.
-  private float vec3X,vec3Y,vec3Z,vec3W,vec3V; // 
-  private float vec4X,vec4Y,vec4Z,vec4W,vec4V; // 
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // no array bounds checking!
   
@@ -92,28 +65,28 @@ public class Dynamic5D extends Dynamic implements Data5D
       
       if( q>1 )
         {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        tmp1.z = nz+pz/q;
-        tmp1.w = nw+pw/q;
-        tmp1.v = nv+pv/q;
+        tmp1.tangent[0] = nx+px/q;
+        tmp1.tangent[1] = ny+py/q;
+        tmp1.tangent[2] = nz+pz/q;
+        tmp1.tangent[3] = nw+pw/q;
+        tmp1.tangent[4] = nv+pv/q;
         }
       else
         {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        tmp1.z = pz+nz*q;
-        tmp1.w = pw+nw*q;
-        tmp1.v = pv+nv*q;
+        tmp1.tangent[0] = px+nx*q;
+        tmp1.tangent[1] = py+ny*q;
+        tmp1.tangent[2] = pz+nz*q;
+        tmp1.tangent[3] = pw+nw*q;
+        tmp1.tangent[4] = pv+nv*q;
         }
       }
     else
       {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      tmp1.z = 0.0f;  
-      tmp1.w = 0.0f;
-      tmp1.v = 0.0f;
+      tmp1.tangent[0] = 0.0f;
+      tmp1.tangent[1] = 0.0f;
+      tmp1.tangent[2] = 0.0f;
+      tmp1.tangent[3] = 0.0f;
+      tmp1.tangent[4] = 0.0f;
       }
     }
     
@@ -126,14 +99,14 @@ public class Dynamic5D extends Dynamic implements Data5D
       tmp1= vc.elementAt(0);
       curr= vv.elementAt(0);
         
-      tmp1.ax = tmp1.ay = tmp1.az = tmp1.aw = tmp1.av = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = tmp1.bw = tmp1.bv = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.cy = curr.y;
-      tmp1.cz = curr.z;
-      tmp1.cw = curr.w;
-      tmp1.cv = curr.v;
-      tmp1.dx = tmp1.dy = tmp1.dz = tmp1.dw = tmp1.dv = 0.0f;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = tmp1.a[3] = tmp1.a[4] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = tmp1.b[3] = tmp1.b[4] = 0.0f;
+      tmp1.c[0] = curr.x;
+      tmp1.c[1] = curr.y;
+      tmp1.c[2] = curr.z;
+      tmp1.c[3] = curr.w;
+      tmp1.c[4] = curr.v;
+      tmp1.d[0] = tmp1.d[1] = tmp1.d[2] = tmp1.d[3] = tmp1.d[4] = 0.0f;
       }
     else if( numPoints==2 )
       {
@@ -142,31 +115,31 @@ public class Dynamic5D extends Dynamic implements Data5D
       curr= vv.elementAt(0);
       next= vv.elementAt(1);
       
-      tmp1.ax = tmp1.ay = tmp1.az = tmp1.aw = tmp1.av = 0.0f;
-      tmp1.bx = tmp1.by = tmp1.bz = tmp1.bw = tmp1.bv = 0.0f;
-      tmp1.cx = next.x - curr.x;
-      tmp1.cy = next.y - curr.y;
-      tmp1.cz = next.z - curr.z;
-      tmp1.cw = next.w - curr.w;
-      tmp1.cv = next.v - curr.v;
-      tmp1.dx = curr.x;
-      tmp1.dy = curr.y;
-      tmp1.dz = curr.z;
-      tmp1.dw = curr.w;
-      tmp1.dv = curr.v;
+      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = tmp1.a[3] = tmp1.a[4] = 0.0f;
+      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = tmp1.b[3] = tmp1.b[4] = 0.0f;
+      tmp1.c[0] = next.x - curr.x;
+      tmp1.c[1] = next.y - curr.y;
+      tmp1.c[2] = next.z - curr.z;
+      tmp1.c[3] = next.w - curr.w;
+      tmp1.c[4] = next.v - curr.v;
+      tmp1.d[0] = curr.x;
+      tmp1.d[1] = curr.y;
+      tmp1.d[2] = curr.z;
+      tmp1.d[3] = curr.w;
+      tmp1.d[4] = curr.v;
       
-      tmp2.ax = tmp2.ay = tmp2.az = tmp2.aw = tmp2.av = 0.0f;
-      tmp2.bx = tmp2.by = tmp2.bz = tmp2.bw = tmp2.bv = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.cy = curr.y - next.y;
-      tmp2.cz = curr.z - next.z;
-      tmp2.cw = curr.w - next.w;
-      tmp2.cv = curr.v - next.v;
-      tmp2.dx = next.x;
-      tmp2.dy = next.y;
-      tmp2.dz = next.z;
-      tmp2.dw = next.w;
-      tmp2.dv = next.v;
+      tmp2.a[0] = tmp2.a[1] = tmp2.a[2] = tmp2.a[3] = tmp2.a[4] = 0.0f;
+      tmp2.b[0] = tmp2.b[1] = tmp2.b[2] = tmp2.b[3] = tmp2.b[4] = 0.0f;
+      tmp2.c[0] = curr.x - next.x;
+      tmp2.c[1] = curr.y - next.y;
+      tmp2.c[2] = curr.z - next.z;
+      tmp2.c[3] = curr.w - next.w;
+      tmp2.c[4] = curr.v - next.v;
+      tmp2.d[0] = next.x;
+      tmp2.d[1] = next.y;
+      tmp2.d[2] = next.z;
+      tmp2.d[3] = next.w;
+      tmp2.d[4] = next.v;
       }
     else
       {
@@ -183,260 +156,42 @@ public class Dynamic5D extends Dynamic implements Data5D
         curr= vv.elementAt(i);
         next= vv.elementAt(n);
       
-        tmp1.vx = curr.x;
-        tmp1.vy = curr.y;
-        tmp1.vz = curr.z;
-        tmp1.vw = curr.w;
-        tmp1.vv = curr.v;
+        tmp1.cached[0] = curr.x;
+        tmp1.cached[1] = curr.y;
+        tmp1.cached[2] = curr.z;
+        tmp1.cached[3] = curr.w;
+        tmp1.cached[4] = curr.v;
         
-        tmp1.ax =  2*curr.x +   tmp1.x - 2*next.x + tmp2.x;
-        tmp1.bx = -3*curr.x - 2*tmp1.x + 3*next.x - tmp2.x;
-        tmp1.cx = tmp1.x;
-        tmp1.dx = curr.x;
+        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
+        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
+        tmp1.c[0] = tmp1.tangent[0];
+        tmp1.d[0] = curr.x;
       
-        tmp1.ay =  2*curr.y +   tmp1.y - 2*next.y + tmp2.y;
-        tmp1.by = -3*curr.y - 2*tmp1.y + 3*next.y - tmp2.y;
-        tmp1.cy = tmp1.y;
-        tmp1.dy = curr.y;
+        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
+        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
+        tmp1.c[1] = tmp1.tangent[1];
+        tmp1.d[1] = curr.y;
       
-        tmp1.az =  2*curr.z +   tmp1.z - 2*next.z + tmp2.z;
-        tmp1.bz = -3*curr.z - 2*tmp1.z + 3*next.z - tmp2.z;
-        tmp1.cz = tmp1.z;
-        tmp1.dz = curr.z;
+        tmp1.a[2] =  2*curr.z +   tmp1.tangent[2] - 2*next.z + tmp2.tangent[2];
+        tmp1.b[2] = -3*curr.z - 2*tmp1.tangent[2] + 3*next.z - tmp2.tangent[2];
+        tmp1.c[2] = tmp1.tangent[2];
+        tmp1.d[2] = curr.z;
         
-        tmp1.aw =  2*curr.w +   tmp1.w - 2*next.w + tmp2.w;
-        tmp1.bw = -3*curr.w - 2*tmp1.w + 3*next.w - tmp2.w;
-        tmp1.cw = tmp1.w;
-        tmp1.dw = curr.w;
+        tmp1.a[3] =  2*curr.w +   tmp1.tangent[3] - 2*next.w + tmp2.tangent[3];
+        tmp1.b[3] = -3*curr.w - 2*tmp1.tangent[3] + 3*next.w - tmp2.tangent[3];
+        tmp1.c[3] = tmp1.tangent[3];
+        tmp1.d[3] = curr.w;
         
-        tmp1.av =  2*curr.v +   tmp1.v - 2*next.v + tmp2.v;
-        tmp1.bv = -3*curr.v - 2*tmp1.v + 3*next.v - tmp2.v;
-        tmp1.cv = tmp1.v;
-        tmp1.dv = curr.v;
+        tmp1.a[4] =  2*curr.v +   tmp1.tangent[4] - 2*next.v + tmp2.tangent[4];
+        tmp1.b[4] = -3*curr.v - 2*tmp1.tangent[4] + 3*next.v - tmp2.tangent[4];
+        tmp1.c[4] = tmp1.tangent[4];
+        tmp1.d[4] = curr.v;
         }
       }
    
     cacheDirty = false;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// v is the speed vector (i.e. position p(t) differentiated by time)
-// a is the acceleration vector (differentiate once more)
-//
-// Now we construct orthogonal basis with Gram-Schmidt:  
-//
-// vec1 = a-delta*v 
-//    where delta = (v*a)/|v|^2
-// vec2 = (0,0,1,0,0) - coeff1*(vx,vy,vz,vw,vv) - coeff2*(vec1x,vec1y,vec1z,vec1w,vec1v)                                     
-//    where coeff1 = vz/|v|^2, coeff2 = vec1Z/|vec1|^2
-// vec3 = (0,0,0,1,0) - coeff1*(vx,vy,vz,vw,vv) - coeff2*(vec1x,vec1y,vec1z,vec1w,vec1v) - coeff3*(vec2x,vec2y,vec2z,vec2w,vec2v)  
-//    where coeff1 = vw/|v|^2, coeff2 = vec1W/|vec1|^2, coeff3 = vec2W/|vec2|^2
-// vec4 = (0,0,0,0,1) - coeff1*(vx,vy,vz,vw,vv) - coeff2*(vec1x,vec1y,vec1z,vec1w,vec1v) - coeff3*(vec2x,vec2y,vec2z,vec2w,vec2v) - coeff4*(vec3x,vec3y,vec3z,vec3w,vec3v) 
-//    where coeff1 = vv/|v|^2, coeff2 = vec1V/|vec1|^2, coeff3 = vec2V/|vec2|^2, coeff4 = vec3V/|vec3|^2
-//
-// this is going to fail if by chance v(t) happens to be one of the standard (0,...,1,...,0) vectors of the orthonormal base!
- 
-  private void setUpVectors(float time,VectorCache vc)
-    {
-    if( vc!=null )
-      {
-      float vx = (3*vc.ax*time+2*vc.bx)*time+vc.cx;
-      float vy = (3*vc.ay*time+2*vc.by)*time+vc.cy;
-      float vz = (3*vc.az*time+2*vc.bz)*time+vc.cz;
-      float vw = (3*vc.aw*time+2*vc.bw)*time+vc.cw;
-      float vv = (3*vc.av*time+2*vc.bv)*time+vc.cv;
-     
-      float ax = 6*vc.ax*time+2*vc.bx;
-      float ay = 6*vc.ay*time+2*vc.by;
-      float az = 6*vc.az*time+2*vc.bz;
-      float aw = 6*vc.aw*time+2*vc.bw;
-      float av = 6*vc.av*time+2*vc.bv;
-     
-      float v_sq = vx*vx+vy*vy+vz*vz+vw*vw+vv*vv;
-      float delta = (vx*ax+vy*ay+vz*az+vw*aw+vv*av)/v_sq;
-     
-      vec1X = ax-delta*vx;
-      vec1Y = ay-delta*vy;
-      vec1Z = az-delta*vz;
-      vec1W = aw-delta*vw;
-      vec1V = av-delta*vv;
-     
-      // construct vec2, vec3 and vec4. Cross product does not work in 5th dimension!
-      float vec1_sq = vec1X*vec1X+vec1Y*vec1Y+vec1Z*vec1Z+vec1W*vec1W+vec1V*vec1V;
-      float coeff21 = vz/v_sq;
-      float coeff22 = vec1Z/vec1_sq;
-      vec2X = 0.0f - coeff21*vx - coeff22*vec1X;
-      vec2Y = 0.0f - coeff21*vy - coeff22*vec1Y;
-      vec2Z = 1.0f - coeff21*vz - coeff22*vec1Z;
-      vec2W = 0.0f - coeff21*vw - coeff22*vec1W;
-      vec2V = 0.0f - coeff21*vv - coeff22*vec1V;
-     
-      float vec2_sq = vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z+vec2W*vec2W+vec2V*vec2V;
-      float coeff31 = vw/v_sq;
-      float coeff32 = vec1W/vec1_sq;
-      float coeff33 = vec2W/vec2_sq;
-      vec3X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-      vec3Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-      vec3Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-      vec3W = 1.0f - coeff31*vw - coeff32*vec1W - coeff33*vec2W;
-      vec3V = 0.0f - coeff31*vv - coeff32*vec1V - coeff33*vec2V;
-     
-      float vec3_sq = vec3X*vec3X+vec3Y*vec3Y+vec3Z*vec3Z+vec3W*vec3W+vec3V*vec3V;
-      float coeff41 = vv/v_sq;
-      float coeff42 = vec1V/vec1_sq;
-      float coeff43 = vec2V/vec2_sq;
-      float coeff44 = vec3V/vec3_sq;
-      vec4X = 0.0f - coeff41*vx - coeff42*vec1X - coeff43*vec2X - coeff44*vec3X;
-      vec4Y = 0.0f - coeff41*vy - coeff42*vec1Y - coeff43*vec2Y - coeff44*vec3Y;
-      vec4Z = 0.0f - coeff41*vz - coeff42*vec1Z - coeff43*vec2Z - coeff44*vec3Z;
-      vec4W = 0.0f - coeff41*vw - coeff42*vec1W - coeff43*vec2W - coeff44*vec3W;
-      vec4V = 1.0f - coeff41*vv - coeff42*vec1V - coeff43*vec2V - coeff44*vec3V;
-     
-      float vec4_sq = vec4X*vec4X+vec4Y*vec4Y+vec4Z*vec4Z+vec4W*vec4W+vec4V*vec4V;
-
-      float len1 = (float)Math.sqrt(v_sq/vec1_sq);   
-      float len2 = (float)Math.sqrt(v_sq/vec2_sq);   
-      float len3 = (float)Math.sqrt(v_sq/vec3_sq);
-      float len4 = (float)Math.sqrt(v_sq/vec4_sq);
-     
-      vec1X*=len1;
-      vec1Y*=len1;
-      vec1Z*=len1;
-      vec1W*=len1;
-      vec1V*=len1;
-     
-      vec2X*=len2;
-      vec2Y*=len2;
-      vec2Z*=len2;
-      vec2W*=len2;
-      vec2V*=len2;
-     
-      vec3X*=len3;
-      vec3Y*=len3;
-      vec3Z*=len3;
-      vec3W*=len3;
-      vec3V*=len3;
-     
-      vec4X*=len4;
-      vec4Y*=len4;
-      vec4Z*=len4;
-      vec4W*=len4;
-      vec4V*=len4;
-      }
-    else
-      {
-      curr = vv.elementAt(0);
-      next = vv.elementAt(1); 
-     
-      float vx = (next.x-curr.x);
-      float vy = (next.y-curr.y);
-      float vz = (next.z-curr.z);
-      float vw = (next.w-curr.w);
-      float vv = (next.v-curr.v);
-     
-      float b = (float)Math.sqrt(vx*vx+vy*vy+vz*vz+vw*vw);
-     
-      if( b>0.0f )
-        {
-        vec1X = vx*vv/b;
-        vec1Y = vy*vv/b;
-        vec1Z = vz*vv/b;
-        vec1W = vw*vv/b;
-        vec1V = -b;
-      
-        float v_sq = vx*vx+vy*vy+vz*vz+vw*vw+vv*vv;
-     
-        // construct vec2, vec3 and vec4. Cross product does not work in 5th dimension!
-        float vec1_sq = vec1X*vec1X+vec1Y*vec1Y+vec1Z*vec1Z+vec1W*vec1W+vec1V*vec1V;
-        float coeff21 = vz/v_sq;
-        float coeff22 = vec1Z/vec1_sq;
-        vec2X = 0.0f - coeff21*vx - coeff22*vec1X;
-        vec2Y = 0.0f - coeff21*vy - coeff22*vec1Y;
-        vec2Z = 1.0f - coeff21*vz - coeff22*vec1Z;
-        vec2W = 0.0f - coeff21*vw - coeff22*vec1W;
-        vec2V = 0.0f - coeff21*vv - coeff22*vec1V;
-
-        float vec2_sq = vec2X*vec2X+vec2Y*vec2Y+vec2Z*vec2Z+vec2W*vec2W+vec2V*vec2V;
-        float coeff31 = vw/v_sq;
-        float coeff32 = vec1W/vec1_sq;
-        float coeff33 = vec2W/vec2_sq;
-        vec3X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-        vec3Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-        vec3Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-        vec3W = 1.0f - coeff31*vw - coeff32*vec1W - coeff33*vec2W;
-        vec3V = 0.0f - coeff31*vv - coeff32*vec1V - coeff33*vec2V;
-     
-        float vec3_sq = vec3X*vec3X+vec3Y*vec3Y+vec3Z*vec3Z+vec3W*vec3W+vec3V*vec3V;
-        float coeff41 = vv/v_sq;
-        float coeff42 = vec1V/vec1_sq;
-        float coeff43 = vec2V/vec2_sq;
-        float coeff44 = vec3V/vec3_sq;
-        vec4X = 0.0f - coeff41*vx - coeff42*vec1X - coeff43*vec2X - coeff44*vec3X;
-        vec4Y = 0.0f - coeff41*vy - coeff42*vec1Y - coeff43*vec2Y - coeff44*vec3Y;
-        vec4Z = 0.0f - coeff41*vz - coeff42*vec1Z - coeff43*vec2Z - coeff44*vec3Z;
-        vec4W = 0.0f - coeff41*vw - coeff42*vec1W - coeff43*vec2W - coeff44*vec3W;
-        vec4V = 1.0f - coeff41*vv - coeff42*vec1V - coeff43*vec2V - coeff44*vec3V;
-     
-        float vec4_sq = vec4X*vec4X+vec4Y*vec4Y+vec4Z*vec4Z+vec4W*vec4W+vec4V*vec4V;
-
-        float len1 = (float)Math.sqrt(v_sq/vec1_sq);    
-        float len2 = (float)Math.sqrt(v_sq/vec2_sq);    
-        float len3 = (float)Math.sqrt(v_sq/vec3_sq);
-        float len4 = (float)Math.sqrt(v_sq/vec4_sq);
-
-        vec1X*=len1;
-        vec1Y*=len1;
-        vec1Z*=len1;
-        vec1W*=len1;
-        vec1V*=len1;
-     
-        vec2X*=len2;
-        vec2Y*=len2;
-        vec2Z*=len2;
-        vec2W*=len2;
-        vec2V*=len2;
-     
-        vec3X*=len3;
-        vec3Y*=len3;
-        vec3Z*=len3;
-        vec3W*=len3;
-        vec3V*=len3;
-     
-        vec4X*=len4;
-        vec4Y*=len4;
-        vec4Z*=len4;
-        vec4W*=len4;
-        vec4V*=len4;
-        }
-      else
-        {
-        vec1X = vv;
-        vec1Y = 0.0f;
-        vec1Z = 0.0f;
-        vec1W = 0.0f;
-        vec1V = 0.0f;
-      
-        vec2X = 0.0f;
-        vec2Y = vv;
-        vec2Z = 0.0f;
-        vec2W = 0.0f;
-        vec2V = 0.0f;
-      
-        vec3X = 0.0f;
-        vec3Y = 0.0f;
-        vec3Z = vv;
-        vec3W = 0.0f;
-        vec3V = 0.0f;
-      
-        vec4X = 0.0f;
-        vec4Y = 0.0f;
-        vec4Z = 0.0f;
-        vec4W = vv;
-        vec4V = 0.0f;
-        }
-      }
-    }
-  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -445,7 +200,8 @@ public class Dynamic5D extends Dynamic implements Data5D
  */
   public Dynamic5D()
     {
-    this(0,0.5f);
+    super(0,0.5f,5);
+    vv = new Vector<>();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -460,15 +216,8 @@ public class Dynamic5D extends Dynamic implements Data5D
  */
   public Dynamic5D(int duration, float count)
     {
+    super(duration,count,5);
     vv = new Vector<>();
-    vc = new Vector<>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = duration;
-    mCount = count;
-    mDimension = 5;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -527,13 +276,13 @@ public class Dynamic5D extends Dynamic implements Data5D
       switch(numPoints)
         {
         case 0: break;
-        case 1: setUpVectors(0.0f,null);
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                 break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(5));
+                vc.add(new VectorCache(5));
+                vc.add(new VectorCache(5));
                 break;
-        default:vc.add(new VectorCache());
+        default:vc.add(new VectorCache(5));
         }
 
       numPoints++;
@@ -559,13 +308,13 @@ public class Dynamic5D extends Dynamic implements Data5D
       switch(numPoints)
         {
         case 0: break;
-        case 1: setUpVectors(0.0f,null);
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
                 break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 2: vc.add(new VectorCache(5));
+                vc.add(new VectorCache(5));
+                vc.add(new VectorCache(5));
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCache(5));
         }
 
       numPoints++;
@@ -597,7 +346,7 @@ public class Dynamic5D extends Dynamic implements Data5D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.remove(n);
         }
@@ -636,7 +385,7 @@ public class Dynamic5D extends Dynamic implements Data5D
         case 1: 
         case 2: break;
         case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
                 break;
         default:vc.removeElementAt(location);
         }
@@ -700,12 +449,12 @@ public class Dynamic5D extends Dynamic implements Data5D
               if( vn!=null )
                 {
                 time = noise(time,0);
-            
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor[0] + vec2X*mFactor[1] + vec3X*mFactor[2] + vec4X*mFactor[3]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor[0] + vec2Y*mFactor[1] + vec3Y*mFactor[2] + vec4Y*mFactor[3]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor[0] + vec2Z*mFactor[1] + vec3Z*mFactor[2] + vec4Z*mFactor[3]);
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (vec1W*mFactor[0] + vec2W*mFactor[1] + vec3W*mFactor[2] + vec4W*mFactor[3]);
-                buffer[offset+4] = (next.v-curr.v)*time + curr.v + (vec1V*mFactor[0] + vec2V*mFactor[1] + vec3V*mFactor[2] + vec4V*mFactor[3]);
+
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1] + baseV[2][0]*mFactor[2] + baseV[3][0]*mFactor[3]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1] + baseV[2][1]*mFactor[2] + baseV[3][1]*mFactor[3]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1] + baseV[2][2]*mFactor[2] + baseV[3][2]*mFactor[3]);
+                buffer[offset+3] = (next.z-curr.z)*time + curr.z + (baseV[0][3]*mFactor[0] + baseV[1][3]*mFactor[1] + baseV[2][3]*mFactor[2] + baseV[3][3]*mFactor[3]);
+                buffer[offset+4] = (next.z-curr.z)*time + curr.z + (baseV[0][4]*mFactor[0] + baseV[1][4]*mFactor[1] + baseV[2][4]*mFactor[2] + baseV[3][4]*mFactor[3]);
                 }
               else
                 {
@@ -755,7 +504,7 @@ public class Dynamic5D extends Dynamic implements Data5D
                   next = vv.elementAt(vecNext);
                   tmp2 = vc.elementAt(vecNext);
               
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w || tmp2.vv!=next.v ) recomputeCache();
+                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z || tmp2.cached[3]!=next.w || tmp2.cached[4]!=next.v ) recomputeCache();
                   }
             
                 tmp1 = vc.elementAt(vecCurr);
@@ -764,21 +513,21 @@ public class Dynamic5D extends Dynamic implements Data5D
                   {
                   time = noise(time,vecCurr);
               
-                  setUpVectors(time,tmp1);
-                 
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor[0] + vec2X*mFactor[1] + vec3X*mFactor[2] + vec4X*mFactor[3]);
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor[0] + vec2Y*mFactor[1] + vec3Y*mFactor[2] + vec4Y*mFactor[3]);
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor[0] + vec2Z*mFactor[1] + vec3Z*mFactor[2] + vec4Z*mFactor[3]);
-                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw + (vec1W*mFactor[0] + vec2W*mFactor[1] + vec3W*mFactor[2] + vec4W*mFactor[3]);
-                  buffer[offset+4]= ((tmp1.av*time+tmp1.bv)*time+tmp1.cv)*time+tmp1.dv + (vec1V*mFactor[0] + vec2V*mFactor[1] + vec3V*mFactor[2] + vec4V*mFactor[3]);
+                  computeOrthonormalBaseMore(time,tmp1);
+
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] + (baseV[0][0]*mFactor[0] + baseV[1][0]*mFactor[1] + baseV[2][0]*mFactor[2] + baseV[3][0]*mFactor[3]);
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] + (baseV[0][1]*mFactor[0] + baseV[1][1]*mFactor[1] + baseV[2][1]*mFactor[2] + baseV[3][1]*mFactor[3]);
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2] + (baseV[0][2]*mFactor[0] + baseV[1][2]*mFactor[1] + baseV[2][2]*mFactor[2] + baseV[3][2]*mFactor[3]);
+                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3] + (baseV[0][3]*mFactor[0] + baseV[1][3]*mFactor[1] + baseV[2][3]*mFactor[2] + baseV[3][3]*mFactor[3]);
+                  buffer[offset+4]= ((tmp1.a[4]*time+tmp1.b[4])*time+tmp1.c[4])*time+tmp1.d[4] + (baseV[0][4]*mFactor[0] + baseV[1][4]*mFactor[1] + baseV[2][4]*mFactor[2] + baseV[3][4]*mFactor[3]);
                   }
                 else
                   {
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy;
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz;
-                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw;
-                  buffer[offset+4]= ((tmp1.av*time+tmp1.bv)*time+tmp1.cv)*time+tmp1.dv;
+                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
+                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
+                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
+                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3];
+                  buffer[offset+4]= ((tmp1.a[4]*time+tmp1.b[4])*time+tmp1.c[4])*time+tmp1.d[4];
                   }
  
                 break;
diff --git a/src/main/java/org/distorted/library/type/DynamicQuat.java b/src/main/java/org/distorted/library/type/DynamicQuat.java
index c974bcf..152eab7 100644
--- a/src/main/java/org/distorted/library/type/DynamicQuat.java
+++ b/src/main/java/org/distorted/library/type/DynamicQuat.java
@@ -33,19 +33,20 @@ public class DynamicQuat extends Dynamic implements Data4D
   {
  
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// Here we implement our own Cache as we need something slightly different.
 // omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
 //  
 // (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
 // still valid and if not - rebuild the Cache
   
-  private class VectorCache
+  private class VectorCacheQuat
     {
     float omega, sinOmega,cosOmega;
     float vx,vy,vz,vw;
     }
   
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
+  private Vector<VectorCacheQuat> vc;
+  private VectorCacheQuat tmp1, tmp2;
 
   private Vector<Static4D> vv;
   private Static4D curr, next;
@@ -190,10 +191,10 @@ public class DynamicQuat extends Dynamic implements Data4D
       switch(numPoints)
          {
          case 0: 
-         case 1: vc.add(new VectorCache());
-                 vc.add(new VectorCache());
+         case 1: vc.add(new VectorCacheQuat());
+                 vc.add(new VectorCacheQuat());
         	     break;
-         default:vc.add(new VectorCache());
+         default:vc.add(new VectorCacheQuat());
          }
 
        numPoints++;
@@ -217,10 +218,10 @@ public class DynamicQuat extends Dynamic implements Data4D
       switch(numPoints)
         {
         case 0: 
-        case 1: vc.add(new VectorCache());
-                vc.add(new VectorCache());
+        case 1: vc.add(new VectorCacheQuat());
+                vc.add(new VectorCacheQuat());
                 break;
-        default:vc.add(location,new VectorCache());
+        default:vc.add(location,new VectorCacheQuat());
         }
 
       numPoints++;
