commit a4835695581f0657cb9b626d1ab8832dba40cc0f
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Wed Jun 15 13:05:55 2016 +0100

    move data types, like FlatND and InterpolatorND, to a separate package.

diff --git a/src/main/java/org/distorted/library/DistortedObject.java b/src/main/java/org/distorted/library/DistortedObject.java
index 45fe9a4..344bae4 100644
--- a/src/main/java/org/distorted/library/DistortedObject.java
+++ b/src/main/java/org/distorted/library/DistortedObject.java
@@ -23,6 +23,17 @@ import android.graphics.Bitmap;
 import android.opengl.GLES20;
 import android.opengl.GLUtils;
 
+import org.distorted.library.type.Float1D;
+import org.distorted.library.type.Float2D;
+import org.distorted.library.type.Float3D;
+import org.distorted.library.type.Float4D;
+import org.distorted.library.type.Interpolator;
+import org.distorted.library.type.Interpolator1D;
+import org.distorted.library.type.Interpolator2D;
+import org.distorted.library.type.Interpolator3D;
+import org.distorted.library.type.Interpolator4D;
+import org.distorted.library.type.InterpolatorQuat;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * All Objects to which Distorted Graphics effects can be applied need to be extended from here.
@@ -658,7 +669,7 @@ public abstract class DistortedObject
     Interpolator1D di = new Interpolator1D();  
     di.setCount(0.5f);
     di.setDuration(duration);
-    di.add(new Float1D(    0));                             
+    di.add(new Float1D(    0));
     di.add(new Float1D(angle));                        
 
     return mM.add(EffectNames.ROTATE, center, di, 0.0f,0.0f,1.0f);
diff --git a/src/main/java/org/distorted/library/EffectMessage.java b/src/main/java/org/distorted/library/EffectMessage.java
index 597bb83..3ce89e3 100644
--- a/src/main/java/org/distorted/library/EffectMessage.java
+++ b/src/main/java/org/distorted/library/EffectMessage.java
@@ -20,7 +20,10 @@
 package org.distorted.library;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
+
+import org.distorted.library.type.Interpolator;
+
+/**
 * Defines all possible events a class implementing the {@link EffectListener} interface can receive.
 */
 
diff --git a/src/main/java/org/distorted/library/EffectQueue.java b/src/main/java/org/distorted/library/EffectQueue.java
index af4c3b5..757ab27 100644
--- a/src/main/java/org/distorted/library/EffectQueue.java
+++ b/src/main/java/org/distorted/library/EffectQueue.java
@@ -19,6 +19,9 @@
 
 package org.distorted.library;
 
+import org.distorted.library.type.Interpolator;
+import org.distorted.library.type.Interpolator2D;
+
 import java.util.Vector;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectQueueFragment.java b/src/main/java/org/distorted/library/EffectQueueFragment.java
index ee2137c..5fb94f4 100644
--- a/src/main/java/org/distorted/library/EffectQueueFragment.java
+++ b/src/main/java/org/distorted/library/EffectQueueFragment.java
@@ -21,6 +21,13 @@ package org.distorted.library;
 
 import android.opengl.GLES20;
 
+import org.distorted.library.type.Float2D;
+import org.distorted.library.type.Float3D;
+import org.distorted.library.type.Float4D;
+import org.distorted.library.type.Interpolator;
+import org.distorted.library.type.Interpolator1D;
+import org.distorted.library.type.Interpolator2D;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class EffectQueueFragment extends EffectQueue
@@ -168,9 +175,21 @@ class EffectQueueFragment extends EffectQueue
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects); 
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = point;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-   
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
       return addBase(eln); 
       }
       
@@ -186,11 +205,23 @@ class EffectQueueFragment extends EffectQueue
       EffectNames.fillWithUnities(eln.ordinal(), mUniforms, NUM_UNIFORMS*mNumEffects);    
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = null;
-      mBuf[4*mNumEffects  ] = point.x;
-      mBuf[4*mNumEffects+1] = point.y;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-   
+      mBuf[4*mNumEffects  ] = point.getX();
+      mBuf[4*mNumEffects+1] = point.getY();
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
       return addBase(eln);
       }
       
@@ -205,12 +236,24 @@ class EffectQueueFragment extends EffectQueue
       {
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = point;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-   
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.z;
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.getZ();
      
       return addBase(eln); 
       }
@@ -226,14 +269,26 @@ class EffectQueueFragment extends EffectQueue
       {
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = null;
-      mBuf[4*mNumEffects  ] = point.x;
-      mBuf[4*mNumEffects+1] = point.y;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-      
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.z;
+      mBuf[4*mNumEffects  ] = point.getX();
+      mBuf[4*mNumEffects+1] = point.getY();
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.getZ();
    
       return addBase(eln);
       }
@@ -249,13 +304,25 @@ class EffectQueueFragment extends EffectQueue
       {
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = point;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-   
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
       mUniforms[NUM_UNIFORMS*mNumEffects+0] = t;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.z;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.getZ();
      
       return addBase(eln); 
       }
@@ -271,15 +338,27 @@ class EffectQueueFragment extends EffectQueue
       {
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = null;
-      mBuf[4*mNumEffects  ] = point.x;
-      mBuf[4*mNumEffects+1] = point.y;
-      mBuf[4*mNumEffects+2] = (region==null || region.z<=0.0f) ? 1000*mObjHalfX : region.z;
-      mBuf[4*mNumEffects+3] = (region==null || region.w<=0.0f) ? 1000*mObjHalfY : region.w;
-      
+      mBuf[4*mNumEffects  ] = point.getX();
+      mBuf[4*mNumEffects+1] = point.getY();
+
+      if( region==null )
+        {
+        mBuf[4*mNumEffects+2] = 1000*mObjHalfX;
+        mBuf[4*mNumEffects+3] = 1000*mObjHalfY;
+        }
+      else
+        {
+        float z = region.getZ();
+        float w = region.getW();
+
+        mBuf[4*mNumEffects+2] = z<=0.0f ? 1000*mObjHalfX : z;
+        mBuf[4*mNumEffects+3] = w<=0.0f ? 1000*mObjHalfY : w;
+        }
+
       mUniforms[NUM_UNIFORMS*mNumEffects+0] = t;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.z;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = c.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = c.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = c.getZ();
    
       return addBase(eln);
       }
diff --git a/src/main/java/org/distorted/library/EffectQueueMatrix.java b/src/main/java/org/distorted/library/EffectQueueMatrix.java
index 6bb0be8..6d303c7 100644
--- a/src/main/java/org/distorted/library/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/EffectQueueMatrix.java
@@ -22,6 +22,11 @@ package org.distorted.library;
 import android.opengl.GLES20;
 import android.opengl.Matrix;
 
+import org.distorted.library.type.Float3D;
+import org.distorted.library.type.Interpolator;
+import org.distorted.library.type.Interpolator1D;
+import org.distorted.library.type.Interpolator3D;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class EffectQueueMatrix extends EffectQueue
@@ -276,9 +281,9 @@ class EffectQueueMatrix extends EffectQueue
       mInterP[mNumEffects] = null;
       mInterI[mNumEffects] = i;
       
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.z;
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.getZ();
             
       return addBase(eln);
       }
@@ -295,9 +300,9 @@ class EffectQueueMatrix extends EffectQueue
       mInterP[mNumEffects] = null;
       mInterI[mNumEffects] = i;
       
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.z;
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.getZ();
       
       mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
       mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
@@ -337,9 +342,9 @@ class EffectQueueMatrix extends EffectQueue
       mInterP[mNumEffects] = null; 
       mInterI[mNumEffects] = null;
       
-      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.y;
-      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.z;
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] = p.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] = p.getY();
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = p.getZ();
       mUniforms[NUM_UNIFORMS*mNumEffects+3] = aA;  
       mUniforms[NUM_UNIFORMS*mNumEffects+4] = aX;
       mUniforms[NUM_UNIFORMS*mNumEffects+5] = aY;  
diff --git a/src/main/java/org/distorted/library/EffectQueueVertex.java b/src/main/java/org/distorted/library/EffectQueueVertex.java
index 4322ad9..0a28285 100644
--- a/src/main/java/org/distorted/library/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/EffectQueueVertex.java
@@ -21,6 +21,11 @@ package org.distorted.library;
 
 import android.opengl.GLES20;
 
+import org.distorted.library.type.Float2D;
+import org.distorted.library.type.Float4D;
+import org.distorted.library.type.Interpolator;
+import org.distorted.library.type.Interpolator2D;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class EffectQueueVertex extends EffectQueue
@@ -173,8 +178,8 @@ class EffectQueueVertex extends EffectQueue
       
       mInterI[mNumEffects] = inter;
       mInterP[mNumEffects] = null;
-      mUniforms[NUM_UNIFORMS*mNumEffects+7] = point.x-mObjHalfX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+8] =-point.y+mObjHalfY;
+      mUniforms[NUM_UNIFORMS*mNumEffects+7] = point.getX()-mObjHalfX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+8] =-point.getY()+mObjHalfY;
      
       return addPriv(eln,region);
       }
@@ -195,8 +200,8 @@ class EffectQueueVertex extends EffectQueue
      
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = null;
-      mUniforms[NUM_UNIFORMS*mNumEffects+7] = point.x-mObjHalfX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+8] =-point.y+mObjHalfY;
+      mUniforms[NUM_UNIFORMS*mNumEffects+7] = point.getX()-mObjHalfX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+8] =-point.getY()+mObjHalfY;
       
       return addPriv(eln,region);    
       }
@@ -210,10 +215,12 @@ class EffectQueueVertex extends EffectQueue
     {    
     if( region!=null )
       {
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = region.x;
-      mUniforms[NUM_UNIFORMS*mNumEffects+4] =-region.y;   // invert y already
-      mUniforms[NUM_UNIFORMS*mNumEffects+5] = region.z<=0.0f ? 1000*mObjHalfX : region.z;
-      mUniforms[NUM_UNIFORMS*mNumEffects+6] = region.w;
+      float z = region.getZ();
+
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] = region.getX();
+      mUniforms[NUM_UNIFORMS*mNumEffects+4] =-region.getY();   // invert y already
+      mUniforms[NUM_UNIFORMS*mNumEffects+5] = z<=0.0f ? 1000*mObjHalfX : z;
+      mUniforms[NUM_UNIFORMS*mNumEffects+6] = region.getW();
       }
     else
       {
diff --git a/src/main/java/org/distorted/library/Float1D.java b/src/main/java/org/distorted/library/Float1D.java
deleted file mode 100644
index 5c3627e..0000000
--- a/src/main/java/org/distorted/library/Float1D.java
+++ /dev/null
@@ -1,90 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * A 1-dimensional data structure containing a single float. The float has no particular meaning; 
- * when this data structure is used in Interpolators, we can think of it as a 1-dimensional Point 
- * a few of which the Interpolator interpolates between.
- */
-
-public class Float1D 
-  {
-  float x;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the single float to ox.   
- *   
- * @param ox value of the single float.
- */
-  public Float1D(int ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the single float to ox.   
- *   
- * @param ox value of the single float.
- */  
-  public Float1D(float ox)
-    {
-    x = ox;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the single float.
- * 
- * @param ox new value of the single float.
- */
-  public void set(int ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the single float.
- * 
- * @param ox new value of the single float.
- */
-  public void set(float ox)
-    {
-    x = ox;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the float contained.
- * 
- * @return The single float.
- */
-  public float getX()
-    {
-    return x;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// end of class   
-  }
diff --git a/src/main/java/org/distorted/library/Float2D.java b/src/main/java/org/distorted/library/Float2D.java
deleted file mode 100644
index fcffdb0..0000000
--- a/src/main/java/org/distorted/library/Float2D.java
+++ /dev/null
@@ -1,98 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * A 2-dimensional data structure containing two floats. The floats have no particular meaning; 
- * when this data structure is used in Interpolators, we can think of it as a 2-dimensional Point 
- * a few of which the Interpolator interpolates between.
- */
-
-public class Float2D extends Float1D
-  {
-  float y;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the two floats to (ox,oy).   
- *   
- * @param ox value of the first float.
- * @param oy value of the second float.
- */  
-  public Float2D(int ox, int oy)
-    {
-    super(ox);
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the two floats to (ox,oy).   
- *   
- * @param ox value of the first float.
- * @param oy value of the second float.
- */    
-  public Float2D(float ox, float oy)
-    {
-    super(ox);
-    y = oy;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (ox,oy).
- * 
- * @param ox new value of the first float
- * @param oy new value of the second float
- */
-  public void set(int ox, int oy)
-    {
-    x = ox;
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (ox,oy).
- * 
- * @param ox new value of the first float
- * @param oy new value of the seond float
- */
-  public void set(float ox, float oy)
-    {
-    x = ox;
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the second float contained.
- * 
- * @return The second float.
- */
-  public float getY()
-    {
-    return y;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// end of class   
-  }
diff --git a/src/main/java/org/distorted/library/Float3D.java b/src/main/java/org/distorted/library/Float3D.java
deleted file mode 100644
index 6197b2a..0000000
--- a/src/main/java/org/distorted/library/Float3D.java
+++ /dev/null
@@ -1,104 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * A 3-dimensional data structure containing three floats. The floats have no particular meaning; 
- * when this data structure is used in Interpolators, we can think of it as a 3-dimensional Point 
- * a few of which the Interpolator interpolates between.
- */
-
-public class Float3D extends Float2D 
-  {
-  float z;
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the three floats to (vx,vy,vz).   
- *   
- * @param vx value of the first float.
- * @param vy value of the second float.
- * @param vz value of the third float.
- */ 
-  public Float3D(int vx, int vy, int vz)
-    {
-    super(vx,vy);
-    z = vz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the three floats to (vx,vy,vz).   
- *   
- * @param vx value of the first float.
- * @param vy value of the second float.
- * @param vz value of the third float.
- */ 
-  public Float3D(float vx, float vy, float vz)
-    {
-    super(vx,vy);
-    z = vz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (vx,vy,vz).
- * 
- * @param vx new value of the first float
- * @param vy new value of the second float
- * @param vz new value of the third float
- */
-  public void set(int vx, int vy, int vz)
-    {
-    x = vx;
-    y = vy;
-    z = vz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (vx,vy,vz).
- * 
- * @param vx new value of the first float
- * @param vy new value of the second float
- * @param vz new value of the third float
- */
-  public void set(float vx, float vy, float vz)
-    {
-    x = vx;
-    y = vy;
-    z = vz;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the third float contained.
- * 
- * @return The third float.
- */
-  public float getZ()
-    {
-    return z;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// end of class   
-  }
diff --git a/src/main/java/org/distorted/library/Float4D.java b/src/main/java/org/distorted/library/Float4D.java
deleted file mode 100644
index e163743..0000000
--- a/src/main/java/org/distorted/library/Float4D.java
+++ /dev/null
@@ -1,110 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * A 4-dimensional data structure containing four floats. The floats have no particular meaning; 
- * when this data structure is used in Interpolators, we can think of it as a 4-dimensional Point 
- * a few of which the Interpolator interpolates between.
- */
-
-public class Float4D extends Float3D 
-  {
-  float w;
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the four floats to (vx,vy,vz,vw).   
- *   
- * @param vx value of the first float.
- * @param vy value of the second float.
- * @param vz value of the third float.
- * @param vw value of the fourth float.
- */ 
-  public Float4D(int vx, int vy, int vz, int vw)
-    {
-    super(vx,vy,vz);
-    w = vw;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the four floats to (vx,vy,vz,vw).   
- *   
- * @param vx value of the first float.
- * @param vy value of the second float.
- * @param vz value of the third float.
- * @param vw value of the fourth float.
- */ 
-  public Float4D(float vx, float vy, float vz, float vw)
-    {
-    super(vx,vy,vz);
-    w = vw;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (vx,vy,vz,vw).
- * 
- * @param vx new value of the first float
- * @param vy new value of the second float
- * @param vz new value of the third float
- * @param vw new value of the fourth float
- */
-  public void set(int vx, int vy, int vz, int vw)
-    {
-    x = vx;
-    y = vy;
-    z = vz;
-    w = vw;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (vx,vy,vz,vw).
- * 
- * @param vx new value of the first float
- * @param vy new value of the second float
- * @param vz new value of the third float
- * @param vw new value of the fourth float
- */
-  public void set(float vx, float vy, float vz, float vw)
-    {
-    x = vx;
-    y = vy;
-    z = vz;
-    w = vw;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the fourth float contained.
- * 
- * @return The fourth float.
- */
-  public float getW()
-    {
-    return w;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// end of class   
-  }
diff --git a/src/main/java/org/distorted/library/Interpolator.java b/src/main/java/org/distorted/library/Interpolator.java
deleted file mode 100644
index 69cfa5b..0000000
--- a/src/main/java/org/distorted/library/Interpolator.java
+++ /dev/null
@@ -1,229 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Random;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** A class to interpolate between a List of Float{1,2,3,4}Ds.
-* <p><ul>
-* <li>if there is only one Point, just jump to it.
-* <li>if there are two Points, linearly bounce between them
-* <li>if there are more, interpolate a loop (or a path!) between them.
-* </ul>
-*/
-
-// The way Interpolation between more than 2 Points is done:
-// 
-// Def: let w[i] = (w[i](x), w[i](y), w[i](z)) be the direction and speed we have to be flying at Point P[i]
-//
-// time it takes to fly though one segment v[i] --> v[i+1] : 0.0 --> 1.0
-// w[i] should be parallel to v[i+1] - v[i-1]   (cyclic notation)
-// |w[i]| proportional to | P[i]-P[i+1] |
-//
-// Given that the flight route (X(t), Y(t), Z(t)) from P(i) to P(i+1)  (0<=t<=1) has to satisfy
-// X(0) = P[i  ](x), Y(0)=P[i  ](y), Z(0)=P[i  ](z), X'(0) = w[i  ](x), Y'(0) = w[i  ](y), Z'(0) = w[i  ](z)
-// X(1) = P[i+1](x), Y(1)=P[i+1](y), Z(1)=P[i+1](z), X'(1) = w[i+1](x), Y'(1) = w[i+1](y), Z'(1) = w[i+1](z)
-//
-// we have the solution:  X(t) = at^3 + bt^2 + ct + d where
-// a =  2*P[i](x) +   w[i](x) - 2*P[i+1](x) + w[i+1](x)
-// b = -3*P[i](x) - 2*w[i](x) + 3*P[i+1](x) - w[i+1](x)
-// c = w[i](x)<br>
-// d = P[i](x)
-//
-// and similarly Y(t) and Z(t).
-
-public abstract class Interpolator 
-  {
-  /**
-   * One revolution takes us from the first vector to the last and back to first through the shortest path. 
-   */
-  public static final int MODE_LOOP = 0; 
-  /**
-   * We come back from the last to the first vector through the same way we got there.
-   */
-  public static final int MODE_PATH = 1; 
-  /**
-   * We just jump back from the last point to the first.
-   */
-  public static final int MODE_JUMP = 2; 
- 
-  protected static Random mRnd = new Random();
-  
-  protected static final int NUM_NOISE = 5; // used iff mNoise>0.0. Number of intermediary points between each pair of adjacent vectors
-                                            // where we randomize noise factors to make the way between the two vectors not so smooth.
-  protected int numPoints;
-  protected int mVecCurr;    
-  protected boolean cacheDirty; // VectorCache not up to date
-  protected int mMode;          // LOOP, PATH or JUMP
-  protected long mDuration;     // number of miliseconds it takes to do a full loop/path from first vector to the last and back to the first 
-  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. 
-  protected float mNoise;       // how 'smooth' our path form each vector to the next is. mNoise = 0.0 (min) --> completely smooth; mNoise==1.0 (max) --> very uneven
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// hide this from Javadoc
-  
-  Interpolator()
-    {
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  public void interpolateMain(float[] buffer, int offset, long currentDuration)
-    {
-    if( mDuration<=0.0f ) 
-      {
-      interpolate(buffer,offset,mCount-(int)mCount);  
-      }
-    else
-      {
-      float x = (float)currentDuration/mDuration;
-           
-      if( x<=mCount || mCount<=0.0f )
-        {
-        interpolate(buffer,offset,x-(int)x);
-        }
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean interpolateMain(float[] buffer, int offset, long currentDuration, long step)
-    {
-    if( mDuration<=0.0f ) 
-      {
-      interpolate(buffer,offset,mCount-(int)mCount);
-      return false;
-      }
-     
-    float x = (float)currentDuration/mDuration;
-           
-    if( x<=mCount || mCount<=0.0f )
-      {
-      interpolate(buffer,offset,x-(int)x);
-        
-      if( currentDuration+step > mDuration*mCount && mCount>0.0f )
-        {
-        interpolate(buffer,offset,mCount-(int)mCount);
-        return true;
-        }
-      }
-    
-    return false;
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// internal debugging only!
-  
-  String print()
-    {
-    return "duration="+mDuration+" count="+mCount+" Noise="+mNoise+" numVectors="+numPoints+" mMode="+mMode;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  abstract void interpolate(float[] buffer, int offset, float time);
-  abstract void createNoise();
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the mode of the interpolation to Loop, Path or Jump.
- * <ul>
- * <li>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.
- * <li>Path is when we come back from the last point back to the first the same way we got there.
- * <li>Jump is when we go from first to last and then jump back to the first.
- * </ul>
- * 
- * @param mode {@link Interpolator#MODE_LOOP}, {@link Interpolator#MODE_PATH} or {@link Interpolator#MODE_JUMP}.
- */
-
-  public void setMode(int mode)
-    {
-    mMode = mode;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the number of Float{1,2,3,4}Ds this Interpolator has been fed with.
- *   
- * @return the number of Float{1,2,3,4}Ds we are currently interpolating through.
- */
-  public synchronized int getNumPoints()
-    {
-    return numPoints;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Controls how many times we want to interpolate.
- * <p>
- * Count equal to 1 means 'go from the first Float{1,2,3,4}D to the last and back'. Does not have to be an
- * integer - i.e. count=1.5 would mean 'start at the first Point, go to the last, come back to the first, 
- * go to the last again and stop'.
- * Count<=0 means 'go on interpolating indefinitely'.
- * 
- * @param count the number of times we want to interpolate between our collection of Float{1,2,3,4}Ds.
- */
-  public void setCount(float count)
-    {
-    mCount = count;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the time it takes to do one full interpolation.
- * 
- * @param duration Time, in milliseconds, it takes to do one full interpolation, i.e. go from the first 
- *                 Point to the last and back. 
- */
-  
-  public void setDuration(long duration)
-    {
-    mDuration = duration;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the 'smoothness' of interpolation. 
- * <p>
- * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible. 
- * Increasing noise makes the Interpolator increasingly deviate from this path, pseudo-randomly speeding 
- * up and slowing down, etc.
- * 
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-  
-  public void setNoise(float noise)
-    {
-    if( mNoise==0.0f && noise != 0.0f )  
-      createNoise();
-   
-    if( mNoise<0.0f ) mNoise = 0.0f;
-    if( mNoise>1.0f ) mNoise = 1.0f;
-   
-    mNoise = noise;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// end of DistortedInterpolator
-  }
diff --git a/src/main/java/org/distorted/library/Interpolator1D.java b/src/main/java/org/distorted/library/Interpolator1D.java
deleted file mode 100644
index 4cce906..0000000
--- a/src/main/java/org/distorted/library/Interpolator1D.java
+++ /dev/null
@@ -1,499 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 1-dimensional implementation of the Interpolator class to interpolate between a list 
-* of Float1Ds.
-*/
-
-public class Interpolator1D extends Interpolator 
-  {
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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 class VectorNoise
-    {
-    float[] nx;
-   
-    public VectorNoise()
-      {
-      nx = new float[NUM_NOISE]; 
-      nx[0] = mRnd.nextFloat();
-      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
-      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
-      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
-      }
-    }
-  
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
- 
-  private Vector<Float1D> vv;
-  private Float1D prev, curr, next;
- 
-  private Vector<VectorNoise> vn;
-  private VectorNoise tmpN;
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void createNoise()
-    {
-    if( vn==null )
-      {
-      vn = new Vector<VectorNoise>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void vec(int c)
-    {
-    int p = c>0 ? c-1: numPoints-1;
-    int n = c<numPoints-1 ? c+1: 0;
-    
-    prev = vv.elementAt(p);
-    curr = vv.elementAt(c);
-    next = vv.elementAt(n);
-
-    tmp1 = vc.elementAt(c);
-    
-    float px = curr.x - prev.x;
-    float nx = next.x - curr.x;
-     
-    float d = nx*nx;
-    
-    if( d>0 )
-      {
-      float q = (float)Math.sqrt((px*px)/d);
-      
-      if( q>1 )
-        {
-        tmp1.x = nx+px/q;
-        }
-      else
-        {
-        tmp1.x = px+nx*q;
-        }
-      }
-    else
-      {
-      tmp1.x = 0.0f;
-      }
-    }
-      
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmp1= vc.elementAt(0);
-      curr= vv.elementAt(0);
-        
-      tmp1.ax = 0.0f;
-      tmp1.bx = 0.0f;
-      tmp1.cx = curr.x;
-      tmp1.dx = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmp1= vc.elementAt(0);
-      tmp2= vc.elementAt(1);
-      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;
-      
-      tmp2.ax = 0.0f;
-      tmp2.bx = 0.0f;
-      tmp2.cx = curr.x - next.x;
-      tmp2.dx = next.x;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) vec(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmp1= vc.elementAt(i);
-        tmp2= vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-    
-        tmp1.vx = 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;
-        }
-      }
-   
-    cacheDirty = false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float noise(float time,int vecNum)
-    {
-    float lower, upper, len;  
-    float d = time*(NUM_NOISE+1);
-    int index = (int)d;
-    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
-    tmpN = vn.elementAt(vecNum);
-   
-    if( index==0 )
-      {
-      len = 1.0f/(NUM_NOISE+1);  
-      return (len + mNoise*(tmpN.nx[0]-len))*d;
-      }
-    if( index==NUM_NOISE )
-      {
-      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
-      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);   
-      return (1.0f-lower)*(d-NUM_NOISE) + lower;   
-      }
-   
-    len = ((float)index)/(NUM_NOISE+1);
-    lower = len + mNoise*(tmpN.nx[index-1]-len);   
-    len = ((float)index+1)/(NUM_NOISE+1); 
-    upper = len + mNoise*(tmpN.nx[index  ]-len);
-            
-    return (upper-lower)*(d-index) + lower; 
-    }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Interpolator1D()
-    {
-    vv = new Vector<Float1D>();
-    vc = new Vector<VectorCache>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = 0;
-    mCount = 0.5f;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Float1D. 
- *   
- * @param location the index of the Point we are interested in.
- * @return The Float1D, if 0<=location&lt;getNumPoints(), or null otherwise. 
- */
-  public synchronized Float1D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * 
- * @param location the index of the Point we are setting.
- * @param x New value of its first float.
- */
-  public synchronized void setPoint(int location, float x)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x);
-        cacheDirty=true;
-        }
-      }
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float1D to the end of our list of Points to interpolate through.
- * <p>   
- * Only a reference to the Point gets added to the List; this means that one can add a Point 
- * here, and later on {@link Float1D#set(float)} it to some new value and the change will
- * be seamlessly reflected in the interpolated path.  
- * <p>
- * A Point can be added multiple times.
- *   
- * @param v The Point to add.
- */
-  public synchronized void add(Float1D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-     
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0: 
-        case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                cacheDirty = true;
-                break;
-        default:vc.add(new VectorCache());
-                cacheDirty = true;
-        }
-     
-      numPoints++;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float1D to the location'th place in our List of Points to interpolate through.  
- *   
- * @param location Index in our List to add the new Point at.
- * @param v The Point to add.
- */
-  public synchronized void add(int location, Float1D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-             
-      switch(numPoints)
-        {
-        case 0:
-        case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                cacheDirty = true;
-                break;
-        default:vc.add(location,new VectorCache());
-                cacheDirty = true;
-        }
-      
-      numPoints++;
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all occurrences of Point v from the List of Points to interpolate through.  
- * 
- * @param v The Point to remove.
- * @return <code>true</code> if we have removed at least one Point.
- */
-  public synchronized boolean remove(Float1D v)
-    {
-    int n = vv.indexOf(v);
-    boolean found = false;
-   
-    while( n>=0 ) 
-      {
-      vv.remove(n);
-     
-      if( vn!=null ) vn.remove(0);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1:
-        case 2: break;
-        case 3: vc.removeAllElements();
-                break;
-        default:vc.remove(n);
-                cacheDirty=true;
-        }
-
-      numPoints--;
-      found = true;
-      n = vv.indexOf(v);
-      }
-   
-    return found;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes a location'th Point from the List of Points we interpolate through.
- * 
- * @param location index of the Point we want to remove. 
- * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
- */
-  public synchronized boolean remove(int location)
-    {
-    if( location>=0 && location<numPoints ) 
-      {
-      vv.removeElementAt(location);
-      
-      if( vn!=null ) vn.remove(0);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                break;
-        default:vc.removeElementAt(location);
-        }
-
-      numPoints--;
-      cacheDirty = true; 
-      return true;
-      }
-
-   return false;
-   }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all Points.
- */
-  public synchronized void removeAll()
-    {
-    numPoints = 0;
-    vv.removeAllElements();
-    vc.removeAllElements();
-    cacheDirty = false;
-   
-    if( vn!=null ) vn.removeAllElements();
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * <p>
- * Since this is a 1-dimensional Interpolator, the resulting interpolated Float1D gets written
- * to a single location in the buffer: buffer[offset]. 
- * 
- * @param buffer Float buffer we will write the resulting Float1D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
- *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
- */
-  public synchronized void interpolate(float[] buffer, int offset, float time)
-    {
-    switch(numPoints)
-      {
-      case 0: buffer[offset] = 0.0f;
-              break;
-      case 1: curr = vv.elementAt(0);
-              buffer[offset] = curr.x;
-              break;
-      case 2: curr = vv.elementAt(0);
-              next = vv.elementAt(1);
-             
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-             
-              if( vn!=null )
-                {
-                time = noise(time,0);
-                }
-             
-              buffer[offset] = (next.x-curr.x)*time + curr.x;
-              break;
-      default:float t = time;
-            
-              switch(mMode)
-                {
-                case MODE_LOOP: time = time*numPoints;
-                                break;
-                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
-                                break;
-                case MODE_JUMP: time = time*(numPoints-1);
-                                break;
-                }
-      
-              int vecCurr = (int)time;
-              time = time-vecCurr;
-      
-              if( vecCurr>=0 && vecCurr<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mVecCurr!= vecCurr )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext;   
-                  mVecCurr = vecCurr;
-                                
-                  switch(mMode)
-                    {
-                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
-                                    break;
-                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
-                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
-                                    break;
-                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
-                                    break;
-                    default       : vecNext = 0;                
-                    }
-              
-                  next = vv.elementAt(vecNext);
-                  tmp2 = vc.elementAt(vecNext);
-              
-                  if( tmp2.vx!=next.x ) recomputeCache();
-                  }
-             
-                if( vn!=null )
-                  {
-                  time = noise(time,vecCurr);
-                  }
-            
-                tmp1 = vc.elementAt(vecCurr);
-                buffer[offset] = ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
-                break;
-                }
-        }
-     }  
-  
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/Interpolator2D.java b/src/main/java/org/distorted/library/Interpolator2D.java
deleted file mode 100644
index 336044c..0000000
--- a/src/main/java/org/distorted/library/Interpolator2D.java
+++ /dev/null
@@ -1,551 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 2-dimensional implementation of the Interpolator class to interpolate between a list 
-* of Float2Ds.
-*/
-
-public class Interpolator2D extends Interpolator 
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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 class VectorNoise
-    {    
-    float[] nx;
-    float[] ny;
-   
-    public VectorNoise()
-      {
-      nx = new float[NUM_NOISE]; 
-      nx[0] = mRnd.nextFloat();
-      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
-      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
-      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
-     
-      ny = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
-      }
-    }
-    
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-   
-  private Vector<Float2D> vv;
-  private Float2D prev, curr, next;
- 
-  private Vector<VectorNoise> vn;
-  private VectorNoise tmpN;
-  
-  private float mFactor;
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void createNoise()
-    {
-    if( vn==null )
-      {
-      vn = new Vector<VectorNoise>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-      }
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void vec(int c)
-    {
-    int p = c>0 ? c-1: numPoints-1;
-    int n = c<numPoints-1 ? c+1: 0;
-    
-    prev = vv.elementAt(p);
-    curr = vv.elementAt(c);
-    next = vv.elementAt(n);
-
-    tmp1 = vc.elementAt(c);
-    
-    float px = curr.x - prev.x;
-    float py = curr.y - prev.y;
-    float nx = next.x - curr.x;
-    float ny = next.y - curr.y;
-     
-    float d = nx*nx+ny*ny;
-    
-    if( d>0 )
-      {
-      float q = (float)Math.sqrt((px*px+py*py)/d);
-      
-      if( q>1 )
-        {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        }
-      else
-        {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        }
-      }
-    else
-      {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      }
-    }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      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;
-      }
-    else if( numPoints==2 )
-      {
-      tmp1= vc.elementAt(0);
-      tmp2= vc.elementAt(1);
-      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;
-      
-      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;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) vec(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmp1= vc.elementAt(i);
-        tmp2= vc.elementAt(n);
-        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;
-        }
-      }
-    
-    cacheDirty = false;
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float noise(float time,int vecNum)
-    {
-    float lower, upper, len;  
-    float d = time*(NUM_NOISE+1);
-    int index = (int)d;
-    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
-    tmpN = vn.elementAt(vecNum);
-   
-    float x = d-index;
-    x = x*x*(3-2*x);
-   
-    switch(index)
-      {
-      case 0        : mFactor = mNoise*tmpN.ny[0]*x;  
-                      return time + mNoise*(d*tmpN.nx[0]-time);                
-      case NUM_NOISE: mFactor= mNoise*tmpN.ny[NUM_NOISE-1]*(1-x);
-                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
-                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
-      default       : float yb = tmpN.ny[index  ];
-                      float ya = tmpN.ny[index-1];
-                      mFactor  = mNoise*((yb-ya)*x+ya);
-   
-                      len = ((float)index)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
-                      len = ((float)index+1)/(NUM_NOISE+1); 
-                      upper = len + mNoise*(tmpN.nx[index  ]-len);
-            
-                      return (upper-lower)*(d-index) + lower; 
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Interpolator2D()
-    {
-    vv = new Vector<Float2D>();
-    vc = new Vector<VectorCache>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = 0;
-    mCount = 0.5f;
-    mNoise = 0.0f;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Float2D. 
- *   
- * @param location the index of the Point we are interested in.
- * @return The Float2D, if 0<=location&lt;getNumPoints(), or null otherwise. 
- */  
-  public synchronized Float2D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * 
- * @param location the index of the Point we are setting.
- * @param x New value of its first float.
- */
-  public synchronized void setPoint(int location, float x, float y)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x,y);
-        cacheDirty=true;
-        }
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float2D to the end of our list of Points to interpolate through.
- * <p>   
- * Only a reference to the Point gets added to the List; this means that one can add a Point 
- * here, and later on {@link Float2D#set(float,float)} it to some new value and the change 
- * will be seamlessly reflected in the interpolated path.  
- * <p>
- * A Point can be added multiple times.
- *   
- * @param v The Point to add.
- */  
-  public synchronized void add(Float2D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-     
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0:
-        case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(new VectorCache());
-        }
-     
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float2D to the location'th place in our List of Points to interpolate through.  
- *   
- * @param location Index in our List to add the new Point at.
- * @param v The Point to add.
- */  
-  public synchronized void add(int location, Float2D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0:
-        case 1: break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(location,new VectorCache());
-        }
-      
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all occurrences of Point v from the List of Points to interpolate through.  
- * 
- * @param v The Point to remove.
- * @return <code>true</code> if we have removed at least one Point.
- */
-  public synchronized boolean remove(Float2D v)
-    {
-    int n = vv.indexOf(v);
-    boolean found = false;
-   
-    while( n>=0 ) 
-      {
-      vv.remove(n);
-     
-      if( vn!=null ) vn.remove(0);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                break;
-        default:vc.remove(n);
-        }
-     
-      numPoints--;
-      found = true;
-      n = vv.indexOf(v);
-      }
-   
-    if( found ) 
-      {
-      cacheDirty=true;
-      }
-   
-    return found;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes a location'th Point from the List of Points we interpolate through.
- * 
- * @param location index of the Point we want to remove. 
- * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
- */
-  public synchronized boolean remove(int location)
-    {
-    if( location>=0 && location<numPoints ) 
-      {
-      vv.removeElementAt(location);
-      
-      if( vn!=null ) vn.remove(0);
-      
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                break;
-        default:vc.removeElementAt(location);
-        }
-
-      numPoints--;
-      cacheDirty = true; 
-      return true;
-      }
-
-   return false;
-   }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all Points.
- */
-  public synchronized void removeAll()
-    {
-    numPoints = 0;
-    vv.removeAllElements();
-    vc.removeAllElements();
-    cacheDirty = false;
-   
-    if( vn!=null ) vn.removeAllElements();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * <p>
- * Since this is a 2-dimensional Interpolator, the resulting interpolated Float2D gets written
- * to two locations in the buffer: buffer[offset] and buffer[offset+1]. 
- * 
- * @param buffer Float buffer we will write the resulting Float2D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
- *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
- */  
-  public synchronized void interpolate(float[] buffer, int offset, float time)
-    {
-    switch(numPoints)
-      {
-      case 0: buffer[offset  ] = 0.0f;
-              buffer[offset+1] = 0.0f;
-              break;
-      case 1: curr = vv.elementAt(0);
-              buffer[offset  ] = curr.x;
-              buffer[offset+1] = curr.y;
-              break;
-      case 2: curr = vv.elementAt(0);
-              next = vv.elementAt(1);
-               
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-             
-              if( vn!=null )
-                {
-                time = noise(time,0);
-              
-                float dx2 = next.x-curr.x;
-                float dy2 = next.y-curr.y;
-   
-                buffer[offset  ] = dx2*time + curr.x +dy2*mFactor;
-                buffer[offset+1] = dy2*time + curr.y -dx2*mFactor;
-                }
-              else
-                {
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
-                }
-              
-              break;
-      default:float t = time;
-        
-              switch(mMode)
-                {
-                case MODE_LOOP: time = time*numPoints;
-                                break;
-                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
-                                break;
-                case MODE_JUMP: time = time*(numPoints-1);
-                                break;
-                }
-            
-              int vecCurr = (int)time;
-              time = time-vecCurr;
-      
-              if( vecCurr>=0 && vecCurr<numPoints )
-                { 
-                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
-                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext;   
-                  mVecCurr = vecCurr;
-                                
-                  switch(mMode)
-                    {
-                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
-                                    break;
-                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
-                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
-                                    break;
-                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
-                                    break;
-                    default       : vecNext = 0;                
-                    }
-              
-                  next = vv.elementAt(vecNext);
-                  tmp2 = vc.elementAt(vecNext);
-              
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y ) recomputeCache();
-                  }
-              
-                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;
-                 
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx +dy2*mFactor;
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy -dx2*mFactor;
-                  } 
-                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;
-                  }
-                
-                break;
-                }
-      }
-    }  
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/Interpolator3D.java b/src/main/java/org/distorted/library/Interpolator3D.java
deleted file mode 100644
index 94c5bb3..0000000
--- a/src/main/java/org/distorted/library/Interpolator3D.java
+++ /dev/null
@@ -1,669 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 3-dimensional implementation of the Interpolator class to interpolate between a list 
-* of Float3Ds.
-*/
-
-public class Interpolator3D extends Interpolator 
-  {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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 class VectorNoise
-    {
-    float[] nx;
-    float[] ny;
-    float[] nz;
-   
-    public VectorNoise()
-      {
-      nx = new float[NUM_NOISE]; 
-      nx[0] = mRnd.nextFloat();
-      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
-      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
-      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
-     
-      ny = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
-     
-      nz = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) nz[i] = mRnd.nextFloat()-0.5f;  
-      }
-    }
-  
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
-  private Vector<Float3D> vv;
-  private Float3D prev, curr, next;
-  
-  private Vector<VectorNoise> vn;
-  private VectorNoise tmpN;
-  
-  private float mFactor1, mFactor2;  // used in Noise only. Those are noise factors; 1=noise of the (vec1X,vec1Y,vec1Z) vector; 2=noise of (vec2X,vec2Y,vec2Z)
-  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.
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void createNoise()
-    {
-    if( vn==null )
-      {  
-      vn = new Vector<VectorNoise>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-      }
-    }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void vec(int c)
-    {
-    int p = c>0 ? c-1: numPoints-1;
-    int n = c<numPoints-1 ? c+1: 0;
-    
-    prev = vv.elementAt(p);
-    curr = vv.elementAt(c);
-    next = vv.elementAt(n);
-
-    tmp1 = vc.elementAt(c);
-    
-    float px = curr.x - prev.x;
-    float py = curr.y - prev.y;
-    float pz = curr.z - prev.z;
-    float nx = next.x - curr.x;
-    float ny = next.y - curr.y;
-    float nz = next.z - curr.z;
-     
-    float d = nx*nx+ny*ny+nz*nz;
-    
-    if( d>0 )
-      {
-      float q = (float)Math.sqrt((px*px+py*py+pz*pz)/d);
-      
-      if( q>1 )
-        {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        tmp1.z = nz+pz/q;
-        }
-      else
-        {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        tmp1.z = pz+nz*q;
-        }
-      }
-    else
-      {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      tmp1.z = 0.0f;  
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      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;
-      }
-    else if( numPoints==2 )
-      {
-      tmp1= vc.elementAt(0);
-      tmp2= vc.elementAt(1);
-      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;
-      
-      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;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) vec(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmp1= vc.elementAt(i);
-        tmp2= vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-      
-        tmp1.vx = curr.x;
-        tmp1.vy = curr.y;
-        tmp1.vz = 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.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.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;
-        }
-      }
-   
-    cacheDirty = false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float noise(float time,int vecNum)
-    {
-    float lower, upper, len;  
-    float d = time*(NUM_NOISE+1);
-    int index = (int)d;
-    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
-    tmpN = vn.elementAt(vecNum);
-   
-    float t = d-index;
-    t = t*t*(3-2*t);
-   
-    switch(index)
-      {
-      case 0        : mFactor1 = mNoise*tmpN.ny[0]*t;
-                      mFactor2 = mNoise*tmpN.nz[0]*t;
-                      return time + mNoise*(d*tmpN.nx[0]-time);
-      case NUM_NOISE: mFactor1= mNoise*tmpN.ny[NUM_NOISE-1]*(1-t);
-                      mFactor2= mNoise*tmpN.nz[NUM_NOISE-1]*(1-t);
-                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
-                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
-      default       : float ya,yb;
-                      yb = tmpN.ny[index  ];
-                      ya = tmpN.ny[index-1];
-                      mFactor1 = mNoise*((yb-ya)*t+ya);
-                      yb = tmpN.nz[index  ];
-                      ya = tmpN.nz[index-1];
-                      mFactor2 = mNoise*((yb-ya)*t+ya);
-   
-                      len = ((float)index)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
-                      len = ((float)index+1)/(NUM_NOISE+1); 
-                      upper = len + mNoise*(tmpN.nx[index  ]-len);
-            
-                      return (upper-lower)*(d-index) + lower; 
-      }
-    }
-     
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Interpolator3D()
-    {
-    vv = new Vector<Float3D>();
-    vc = new Vector<VectorCache>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = 0;
-    mCount = 0.5f;
-    mNoise = 0.0f;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Float3D. 
- *   
- * @param location the index of the Point we are interested in.
- * @return The Float3D, if 0<=location&lt;getNumPoints(), or null otherwise. 
- */  
-  public synchronized Float3D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * 
- * @param location the index of the Point we are setting.
- * @param x New value of its first float.
- */
-  public synchronized void setPoint(int location, float x, float y, float z)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x,y,z);
-        cacheDirty=true;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float3D to the end of our list of Points to interpolate through.
- * <p>   
- * Only a reference to the Point gets added to the List; this means that one can add a Point 
- * here, and later on {@link Float3D#set(float,float,float)} it to some new value and the 
- * change will be seamlessly reflected in the interpolated path.  
- * <p>
- * A Point can be added multiple times.
- *   
- * @param v The Point to add.
- */    
-  public synchronized void add(Float3D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-        
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: setUpVectors(0.0f,null);
-                break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(new VectorCache());
-        }
-
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float3D to the location'th place in our List of Points to interpolate through.  
- *   
- * @param location Index in our List to add the new Point at.
- * @param v The Point to add.
- */  
-  public synchronized void add(int location, Float3D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: setUpVectors(0.0f,null);
-                break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(location,new VectorCache());
-        }
-
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all occurrences of Point v from the List of Points to interpolate through.  
- * 
- * @param v The Point to remove.
- * @return <code>true</code> if we have removed at least one Point.
- */
-  public synchronized boolean remove(Float3D v)
-    {
-    int n = vv.indexOf(v);
-    boolean found = false;
-   
-    while( n>=0 ) 
-      {
-      vv.remove(n);
-     
-      if( vn!=null ) vn.remove(0);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
-                break;
-        default:vc.remove(n);
-        }
-
-      numPoints--;
-      found = true;
-      n = vv.indexOf(v);
-      }
-   
-    if( found ) 
-      {
-      cacheDirty=true;
-      }
-   
-    return found;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes a location'th Point from the List of Points we interpolate through.
- * 
- * @param location index of the Point we want to remove. 
- * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
- */
-  public synchronized boolean remove(int location)
-    {
-    if( location>=0 && location<numPoints ) 
-      {
-      vv.removeElementAt(location);
-       
-      if( vn!=null ) vn.remove(0);
-      
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
-                break;
-        default:vc.removeElementAt(location);
-        }
-
-      numPoints--;
-      cacheDirty = true; 
-      return true;
-      }
-
-   return false;
-   }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all Points.
- */
-  public synchronized void removeAll()
-    {
-    numPoints = 0;
-    vv.removeAllElements();
-    vc.removeAllElements();
-    cacheDirty = false;
-   
-    if( vn!=null ) vn.removeAllElements();
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * <p>
- * Since this is a 3-dimensional Interpolator, the resulting interpolated Float3D gets written
- * to three locations in the buffer: buffer[offset], buffer[offset+1] and buffer[offset+2]. 
- * 
- * @param buffer Float buffer we will write the resulting Float3D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
- *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
- */    
-  public synchronized void interpolate(float[] buffer, int offset, float time)
-    {  
-    switch(numPoints)
-      {
-      case 0: buffer[offset  ] = 0.0f;
-              buffer[offset+1] = 0.0f;
-              buffer[offset+2] = 0.0f;
-              break;
-      case 1: curr = vv.elementAt(0);
-              buffer[offset  ] = curr.x;
-              buffer[offset+1] = curr.y;
-              buffer[offset+2] = curr.z;
-              break;
-      case 2: curr = vv.elementAt(0);
-              next = vv.elementAt(1);
-             
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-             
-              if( vn!=null )
-                {
-                time = noise(time,0);
-            
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor1 + vec2X*mFactor2);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor1 + vec2Y*mFactor2);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor1 + vec2Z*mFactor2); 
-                }
-              else
-                {
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
-                }
-             
-              break;
-      default:float t = time;
-        
-              switch(mMode)
-                {
-                case MODE_LOOP: time = time*numPoints;
-                                break;
-                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
-                                break;
-                case MODE_JUMP: time = time*(numPoints-1);
-                                break;
-                }
-           
-              int vecCurr = (int)time;
-              time = time-vecCurr;
-      
-              if( vecCurr>=0 && vecCurr<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
-                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext;   
-                  mVecCurr = vecCurr;
-                       
-                  switch(mMode)
-                    {
-                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
-                                    break;
-                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
-                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
-                                    break;
-                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
-                                    break;
-                    default       : vecNext = 0;                
-                    }
-              
-                  next = vv.elementAt(vecNext);
-                  tmp2 = vc.elementAt(vecNext);
-              
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z ) recomputeCache();
-                  }
-            
-                tmp1 = vc.elementAt(vecCurr);
-               
-                if( vn!=null )
-                  {
-                  time = noise(time,vecCurr);
-              
-                  setUpVectors(time,tmp1);
-                 
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor1 + vec2X*mFactor2);
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor1 + vec2Y*mFactor2);
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor1 + vec2Z*mFactor2);
-                  }
-                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;
-                  }
-               
-                break;
-                }
-       }
-     }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/Interpolator4D.java b/src/main/java/org/distorted/library/Interpolator4D.java
deleted file mode 100644
index c11ca57..0000000
--- a/src/main/java/org/distorted/library/Interpolator4D.java
+++ /dev/null
@@ -1,771 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 4-dimensional implementation of the Interpolator class to interpolate between a list 
-* of Float4Ds.
-*/
-
-public class Interpolator4D extends Interpolator 
-  {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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 class VectorNoise
-    {
-    float[] nx;
-    float[] ny;
-    float[] nz;
-    float[] nw;
-   
-    public VectorNoise()
-      {
-      nx = new float[NUM_NOISE]; 
-      nx[0] = mRnd.nextFloat();
-      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1] + mRnd.nextFloat();
-      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
-      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
-     
-      ny = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
-     
-      nz = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) nz[i] = mRnd.nextFloat()-0.5f;
-     
-      nw = new float[NUM_NOISE];
-      for(int i=0; i<NUM_NOISE; i++) nw[i] = mRnd.nextFloat()-0.5f;  
-      }
-    }
-  
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
-  private Vector<Float4D> vv;
-  private Float4D prev, curr, next;
-  
-  private Vector<VectorNoise> vn;
-  private VectorNoise tmpN;
-  
-  private float mFactor1, mFactor2, mFactor3; // used in Noise only. Those are noise factors; 1=noise of the (vec1X,vec1Y,vec1Z,vec1W) vector; 2=noise of (vec2X,vec2Y,vec2Z,vec2W) and same for vec3.
-  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.
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void createNoise()
-    {
-    if( vn==null )
-      {  
-      vn = new Vector<VectorNoise>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-      }
-    }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void vec(int c)
-    {
-    int p = c>0 ? c-1: numPoints-1;
-    int n = c<numPoints-1 ? c+1: 0;
-    
-    prev = vv.elementAt(p);
-    curr = vv.elementAt(c);
-    next = vv.elementAt(n);
-
-    tmp1 = vc.elementAt(c);
-    
-    float px = curr.x - prev.x;
-    float py = curr.y - prev.y;
-    float pz = curr.z - prev.z;
-    float pw = curr.w - prev.w;
-    float nx = next.x - curr.x;
-    float ny = next.y - curr.y;
-    float nz = next.z - curr.z;
-    float nw = next.w - curr.w;
-     
-    float d = nx*nx+ny*ny+nz*nz+nw*nw;
-    
-    if( d>0 )
-      {
-      float q = (float)Math.sqrt((px*px+py*py+pz*pz+pw*pw)/d);
-      
-      if( q>1 )
-        {
-        tmp1.x = nx+px/q;
-        tmp1.y = ny+py/q;
-        tmp1.z = nz+pz/q;
-        tmp1.w = nw+pw/q;
-        }
-      else
-        {
-        tmp1.x = px+nx*q;
-        tmp1.y = py+ny*q;
-        tmp1.z = pz+nz*q;
-        tmp1.w = pw+nw*q;
-        }
-      }
-    else
-      {
-      tmp1.x = 0.0f;
-      tmp1.y = 0.0f;
-      tmp1.z = 0.0f;  
-      tmp1.w = 0.0f;
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      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;
-      }
-    else if( numPoints==2 )
-      {
-      tmp1= vc.elementAt(0);
-      tmp2= vc.elementAt(1);
-      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;
-      
-      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;
-      }
-    else
-      {
-      int i, n;  
-      
-      for(i=0; i<numPoints; i++) vec(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmp1= vc.elementAt(i);
-        tmp2= vc.elementAt(n);
-        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.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.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.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;
-        }
-      }
-   
-    cacheDirty = false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float noise(float time,int vecNum)
-    {
-    float lower, upper, len;  
-    float d = time*(NUM_NOISE+1);
-    int index = (int)d;
-    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
-    tmpN = vn.elementAt(vecNum);
-   
-    float t = d-index;
-    t = t*t*(3-2*t);
-   
-    switch(index)
-      {
-      case 0        : mFactor1 = mNoise*tmpN.ny[0]*t;
-                      mFactor2 = mNoise*tmpN.nz[0]*t;
-                      mFactor3 = mNoise*tmpN.nw[0]*t;
-                      return time + mNoise*(d*tmpN.nx[0]-time);
-      case NUM_NOISE: mFactor1= mNoise*tmpN.ny[NUM_NOISE-1]*(1-t);
-                      mFactor2= mNoise*tmpN.nz[NUM_NOISE-1]*(1-t);
-                      mFactor3= mNoise*tmpN.nw[NUM_NOISE-1]*(1-t);
-                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
-                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
-      default       : float ya,yb;
-                      yb = tmpN.ny[index  ];
-                      ya = tmpN.ny[index-1];
-                      mFactor1 = mNoise*((yb-ya)*t+ya);
-                      yb = tmpN.nz[index  ];
-                      ya = tmpN.nz[index-1];
-                      mFactor2 = mNoise*((yb-ya)*t+ya);
-                      yb = tmpN.nw[index  ];
-                      ya = tmpN.nw[index-1];
-                      mFactor3 = mNoise*((yb-ya)*t+ya);
-   
-                      len = ((float)index)/(NUM_NOISE+1);
-                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
-                      len = ((float)index+1)/(NUM_NOISE+1); 
-                      upper = len + mNoise*(tmpN.nx[index  ]-len);
-            
-                      return (upper-lower)*(d-index) + lower; 
-      }
-    }
-     
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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*vw)/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;
-      vec2X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-      vec2Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-      vec2Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-      vec2W = 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;
-        vec2X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
-        vec2Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
-        vec2Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
-        vec2W = 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
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Interpolator4D()
-    {
-    vv = new Vector<Float4D>();
-    vc = new Vector<VectorCache>();
-    vn = null;
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = 0;
-    mCount = 0.5f;
-    mNoise = 0.0f;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Float4D. 
- *   
- * @param location the index of the Point we are interested in.
- * @return The Float4D, if 0<=location&lt;getNumPoints(), or null otherwise. 
- */  
-  public synchronized Float4D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * 
- * @param location the index of the Point we are setting.
- * @param x New value of its first float.
- */
-  public synchronized void setPoint(int location, float x, float y, float z, float w)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x,y,z,w);
-        cacheDirty=true;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float4D to the end of our list of Points to interpolate through.
- * <p>   
- * Only a reference to the Point gets added to the List; this means that one can add a Point 
- * here, and later on {@link Float4D#set(float,float,float,float)} it to some new value and 
- * the change will be seamlessly reflected in the interpolated path.  
- * <p>
- * A Point can be added multiple times.
- *   
- * @param v The Point to add.
- */    
-  public synchronized void add(Float4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-        
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-       switch(numPoints)
-         {
-         case 0: break;
-         case 1: setUpVectors(0.0f,null);
-                 break;
-         case 2: vc.add(new VectorCache());
-                 vc.add(new VectorCache());
-                 vc.add(new VectorCache());
-                 break;
-         default:vc.add(new VectorCache());
-         }
-
-       numPoints++;
-       cacheDirty = true;
-       }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float4D to the location'th place in our List of Points to interpolate through.  
- *   
- * @param location Index in our List to add the new Point at.
- * @param v The Float4D to add.
- */  
-  public synchronized void add(int location, Float4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: setUpVectors(0.0f,null);
-                break;
-        case 2: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(location,new VectorCache());
-        }
-
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all occurrences of Point v from the List of Points to interpolate through.  
- * 
- * @param v The Point to remove.
- * @return <code>true</code> if we have removed at least one Point.
- */
-  public synchronized boolean remove(Float4D v)
-    {
-    int n = vv.indexOf(v);
-    boolean found = false;
-   
-    while( n>=0 ) 
-      {
-      vv.remove(n);
-     
-      if( vn!=null ) vn.remove(0);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
-                break;
-        default:vc.remove(n);
-        }
-
-      numPoints--;
-      found = true;
-      n = vv.indexOf(v);
-      }
-   
-    if( found ) 
-      {
-      cacheDirty=true;
-      }
-   
-    return found;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes a location'th Point from the List of Points we interpolate through.
- * 
- * @param location index of the Point we want to remove. 
- * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
- */
-  public synchronized boolean remove(int location)
-    {
-    if( location>=0 && location<numPoints ) 
-      {
-      vv.removeElementAt(location);
-       
-      if( vn!=null ) vn.remove(0);
-      
-      switch(numPoints)
-        {
-        case 0:
-        case 1: 
-        case 2: break;
-        case 3: vc.removeAllElements();
-                setUpVectors(0.0f,null);
-                break;
-        default:vc.removeElementAt(location);
-        }
-
-      numPoints--;
-      cacheDirty = true; 
-      return true;
-      }
-
-    return false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all Points.
- */
-  public synchronized void removeAll()
-    {
-    numPoints = 0;
-    vv.removeAllElements();
-    vc.removeAllElements();
-    cacheDirty = false;
-   
-    if( vn!=null ) vn.removeAllElements();
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * <p>
- * Since this is a 4-dimensional Interpolator, the resulting interpolated Float4D gets written
- * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
- * 
- * @param buffer Float buffer we will write the resulting Float4D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
- *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
- */    
-  public synchronized void interpolate(float[] buffer, int offset, float time)
-    {  
-    switch(numPoints)
-      {
-      case 0: buffer[offset  ] = 0.0f;
-              buffer[offset+1] = 0.0f;
-              buffer[offset+2] = 0.0f;
-              buffer[offset+3] = 0.0f;
-              break;
-      case 1: curr = vv.elementAt(0);
-              buffer[offset  ] = curr.x;
-              buffer[offset+1] = curr.y;
-              buffer[offset+2] = curr.z;
-              buffer[offset+3] = curr.w;
-              break;
-      case 2: curr = vv.elementAt(0);
-              next = vv.elementAt(1);
-            
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-             
-              if( vn!=null )
-                {
-                time = noise(time,0);
-            
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor1 + vec2X*mFactor2 + vec3X*mFactor3);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor1 + vec2Y*mFactor2 + vec3Y*mFactor3);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor1 + vec2Z*mFactor2 + vec3Z*mFactor3);
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (vec1W*mFactor1 + vec2W*mFactor2 + vec3W*mFactor3); 
-                }
-              else
-                {
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w;
-                }
-                
-              break;
-      default:float t = time;
-        
-              switch(mMode)
-                {
-                case MODE_LOOP: time = time*numPoints;
-                                break;
-                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
-                                break;
-                case MODE_JUMP: time = time*(numPoints-1);
-                                break;
-                }
-     
-              int vecCurr = (int)time;
-              time = time-vecCurr;
-      
-              if( vecCurr>=0 && vecCurr<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
-                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext;   
-                  mVecCurr = vecCurr;
-                       
-                  switch(mMode)
-                    {
-                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
-                                    break;
-                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
-                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
-                                    break;
-                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
-                                    break;
-                    default       : vecNext = 0;                
-                    }
-     
-                  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();
-                  }
-            
-                tmp1 = vc.elementAt(vecCurr);
-               
-                if( vn!=null )
-                  {
-                  time = noise(time,vecCurr);
-              
-                  setUpVectors(time,tmp1);
-                 
-                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor1 + vec2X*mFactor2 + vec3X*mFactor3);
-                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor1 + vec2Y*mFactor2 + vec3Y*mFactor3);
-                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor1 + vec2Z*mFactor2 + vec3Z*mFactor3);
-                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw + (vec1W*mFactor1 + vec2W*mFactor2 + vec3W*mFactor3);
-                  }
-                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;
-                  }
- 
-                break;
-                }
-      }
-    }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/InterpolatorQuat.java b/src/main/java/org/distorted/library/InterpolatorQuat.java
deleted file mode 100644
index 25b2c26..0000000
--- a/src/main/java/org/distorted/library/InterpolatorQuat.java
+++ /dev/null
@@ -1,392 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 4-dimensional implementation of the Interpolator class to interpolate between a list 
-* of Float4Ds.
-* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
-* SLERP. Noise not supported (yet?).
-*/
-
-public class InterpolatorQuat extends Interpolator 
-  {
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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
-    {
-    float omega, sinOmega,cosOmega;
-    float vx,vy,vz,vw;
-    }
-  
-  private Vector<VectorCache> vc;
-  private VectorCache tmp1, tmp2;
-
-  private Vector<Float4D> vv;
-  private Float4D curr, next;
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//Abramowitz / Stegun
-
-  private static float arcCos(float x)
-    {
-    if( x<0 )
-      return 3.14159265358979f - (float)Math.sqrt(1+x)*(1.5707288f + 0.2121144f*x + 0.074261f*x*x + 0.0187293f*x*x*x);
-     
-    return (float)Math.sqrt(1-x)*(1.5707288f - 0.2121144f*x + 0.074261f*x*x - 0.0187293f*x*x*x);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Quaternion Interpolator doesn't support noise
-  
-  synchronized void createNoise()
-    {
-
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints>=2 )
-      {
-      int i, n;  
-     
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmp1= vc.elementAt(i);
-        tmp2= vc.elementAt(n);
-        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.cosOmega = curr.x*next.x + curr.y*next.y + curr.z*next.z + curr.w*next.w;
-      	
-        if( tmp1.cosOmega<0 && n!=0 )  // do not invert the last quaternion even if we'd have to go the long way around!
-          {
-          tmp1.cosOmega = -tmp1.cosOmega;
-          next.x = -next.x;
-          next.y = -next.y;
-          next.z = -next.z;
-          next.w = -next.w;
-          }
-      	
-        tmp1.sinOmega = (float)Math.sqrt(1-tmp1.cosOmega*tmp1.cosOmega);
-        tmp1.omega = arcCos(tmp1.cosOmega);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public InterpolatorQuat()
-    {
-    vv = new Vector<Float4D>();
-    vc = new Vector<VectorCache>();
-    numPoints = 0;
-    cacheDirty = false;
-    mMode = MODE_LOOP;
-    mDuration = 0;
-    mCount = 0.5f;
-    mNoise = 0.0f;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Float4D. 
- *   
- * @param location the index of the Point we are interested in.
- * @return The Float4D, if 0<=location&lt;getNumPoints(), or null otherwise. 
- */  
-  public synchronized Float4D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * 
- * @param location the index of the Point we are setting.
- * @param x New value of its first float.
- */
-  public synchronized void setPoint(int location, float x, float y, float z, float w)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x,y,z,w);
-        cacheDirty=true;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float4D to the end of our list of Points to interpolate through.
- * <p>   
- * Only a reference to the Point gets added to the List; this means that one can add a Point 
- * here, and later on {@link Float4D#set(float,float,float,float)} it to some new value and 
- * the change will be seamlessly reflected in the interpolated path.  
- * <p>
- * A Point can be added multiple times.
- *   
- * @param v The Point to add.
- */    
-  public synchronized void add(Float4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-      
-      switch(numPoints)
-         {
-         case 0: 
-         case 1: vc.add(new VectorCache());
-                 vc.add(new VectorCache());
-        	     break;
-         default:vc.add(new VectorCache());
-         }
-
-       numPoints++;
-       cacheDirty = true;
-       }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Float4D to the location'th place in our List of Points to interpolate through.  
- *   
- * @param location Index in our List to add the new Point at.
- * @param v The Float4D to add.
- */  
-  public synchronized void add(int location, Float4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      switch(numPoints)
-        {
-        case 0: 
-        case 1: vc.add(new VectorCache());
-                vc.add(new VectorCache());
-                break;
-        default:vc.add(location,new VectorCache());
-        }
-
-      numPoints++;
-      cacheDirty = true;
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all occurrences of Point v from the List of Points to interpolate through.  
- * 
- * @param v The Point to remove.
- * @return <code>true</code> if we have removed at least one Point.
- */
-  public synchronized boolean remove(Float4D v)
-    {
-    int n = vv.indexOf(v);
-    boolean found = false;
-   
-    while( n>=0 ) 
-      {
-      vv.remove(n);
-     
-      switch(numPoints)
-        {
-        case 0:
-        case 1: break;
-        case 2: vc.removeAllElements();
-                break;
-        default:vc.remove(n);
-        }
-
-      numPoints--;
-      found = true;
-      n = vv.indexOf(v);
-      }
-   
-    if( found ) 
-      {
-      cacheDirty=true;
-      }
-   
-    return found;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes a location'th Point from the List of Points we interpolate through.
- * 
- * @param location index of the Point we want to remove. 
- * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
- */
-  public synchronized boolean remove(int location)
-    {
-    if( location>=0 && location<numPoints ) 
-      {
-      vv.removeElementAt(location);
-      
-      switch(numPoints)
-        {
-        case 0: 
-        case 1: break;
-        case 2: vc.removeAllElements();
-                break;
-        default:vc.removeElementAt(location);
-        }
-
-      numPoints--;
-      cacheDirty = true; 
-      return true;
-      }
-
-    return false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all Points.
- */
-  public synchronized void removeAll()
-    {
-    numPoints = 0;
-    vv.removeAllElements();
-    vc.removeAllElements();
-    cacheDirty = false;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * Interpolation is done using the spherical linear algorithm, aka SLERP.
- * <p>
- * Since this is a 4-dimensional Interpolator, the resulting interpolated Float4D gets written
- * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
- * 
- * @param buffer Float buffer we will write the resulting Float4D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
- *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
- */    
-  public synchronized void interpolate(float[] buffer, int offset, float time)
-    {  
-    switch(numPoints)
-      {
-      case 0: buffer[offset  ] = 0.0f;
-              buffer[offset+1] = 0.0f;
-              buffer[offset+2] = 0.0f;
-              buffer[offset+3] = 0.0f;
-              break;
-      case 1: curr = vv.elementAt(0);
-              buffer[offset  ] = curr.x; 
-              buffer[offset+1] = curr.y;
-              buffer[offset+2] = curr.z;
-              buffer[offset+3] = curr.w;
-              break;
-      default:float t = time;
-              float scale0, scale1;
-  
-              if( mMode==MODE_JUMP ) time = time*(numPoints-1);
-              else if( mMode==MODE_PATH || numPoints==2 ) time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
-              else time = time*numPoints;
-              
-              int vecNext, vecCurr = (int)time;
-              time = time-vecCurr;
-      
-              if( vecCurr>=0 && vecCurr<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
-                   
-                switch(mMode)
-                  {
-                  case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
-                                  break;
-                  case MODE_PATH: if( t<0.5f ) vecNext = vecCurr+1;  
-                                  else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
-                                  break;
-                  case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
-                                  break;
-                  default       : vecNext = 0;                
-                  }
-     
-                curr = vv.elementAt(vecCurr);
-                next = vv.elementAt(vecNext);
-                tmp1 = vc.elementAt(vecCurr);
-                tmp2 = vc.elementAt(vecNext);
-              
-                if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
-               
-                if( tmp1.sinOmega==0 )
-                  {
-                  scale0 = 0f;
-                  scale1 = 1f;
-                  }
-                else if( tmp1.cosOmega < 0.99 ) 
-                  {
-                  scale0 = (float)Math.sin( (1f-time)*tmp1.omega ) / tmp1.sinOmega;
-                  scale1 = (float)Math.sin(     time *tmp1.omega ) / tmp1.sinOmega;
-                  }
-                else 
-                  {
-                  scale0 = 1f-time;
-                  scale1 = time;
-                  }
-
-                buffer[offset  ] = scale0*curr.x + scale1*next.x;
-                buffer[offset+1] = scale0*curr.y + scale1*next.y; 
-                buffer[offset+2] = scale0*curr.z + scale1*next.z; 
-                buffer[offset+3] = scale0*curr.w + scale1*next.w; 
-                
-                break;
-                }
-      }
-    }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Float1D.java b/src/main/java/org/distorted/library/type/Float1D.java
new file mode 100644
index 0000000..1cd6fcc
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Float1D.java
@@ -0,0 +1,90 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A 1-dimensional data structure containing a single float. The float has no particular meaning; 
+ * when this data structure is used in Interpolators, we can think of it as a 1-dimensional Point 
+ * a few of which the Interpolator interpolates between.
+ */
+
+public class Float1D 
+  {
+  float x;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the single float to ox.   
+ *   
+ * @param ox value of the single float.
+ */
+  public Float1D(int ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the single float to ox.   
+ *   
+ * @param ox value of the single float.
+ */  
+  public Float1D(float ox)
+    {
+    x = ox;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the single float.
+ * 
+ * @param ox new value of the single float.
+ */
+  public void set(int ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the single float.
+ * 
+ * @param ox new value of the single float.
+ */
+  public void set(float ox)
+    {
+    x = ox;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the float contained.
+ * 
+ * @return The single float.
+ */
+  public float getX()
+    {
+    return x;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// end of class   
+  }
diff --git a/src/main/java/org/distorted/library/type/Float2D.java b/src/main/java/org/distorted/library/type/Float2D.java
new file mode 100644
index 0000000..b734a94
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Float2D.java
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A 2-dimensional data structure containing two floats. The floats have no particular meaning; 
+ * when this data structure is used in Interpolators, we can think of it as a 2-dimensional Point 
+ * a few of which the Interpolator interpolates between.
+ */
+
+public class Float2D extends Float1D
+  {
+  float y;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the two floats to (ox,oy).   
+ *   
+ * @param ox value of the first float.
+ * @param oy value of the second float.
+ */  
+  public Float2D(int ox, int oy)
+    {
+    super(ox);
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the two floats to (ox,oy).   
+ *   
+ * @param ox value of the first float.
+ * @param oy value of the second float.
+ */    
+  public Float2D(float ox, float oy)
+    {
+    super(ox);
+    y = oy;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (ox,oy).
+ * 
+ * @param ox new value of the first float
+ * @param oy new value of the second float
+ */
+  public void set(int ox, int oy)
+    {
+    x = ox;
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (ox,oy).
+ * 
+ * @param ox new value of the first float
+ * @param oy new value of the seond float
+ */
+  public void set(float ox, float oy)
+    {
+    x = ox;
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the second float contained.
+ * 
+ * @return The second float.
+ */
+  public float getY()
+    {
+    return y;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// end of class   
+  }
diff --git a/src/main/java/org/distorted/library/type/Float3D.java b/src/main/java/org/distorted/library/type/Float3D.java
new file mode 100644
index 0000000..11d17cd
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Float3D.java
@@ -0,0 +1,104 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A 3-dimensional data structure containing three floats. The floats have no particular meaning; 
+ * when this data structure is used in Interpolators, we can think of it as a 3-dimensional Point 
+ * a few of which the Interpolator interpolates between.
+ */
+
+public class Float3D extends Float2D 
+  {
+  float z;
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the three floats to (vx,vy,vz).   
+ *   
+ * @param vx value of the first float.
+ * @param vy value of the second float.
+ * @param vz value of the third float.
+ */ 
+  public Float3D(int vx, int vy, int vz)
+    {
+    super(vx,vy);
+    z = vz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the three floats to (vx,vy,vz).   
+ *   
+ * @param vx value of the first float.
+ * @param vy value of the second float.
+ * @param vz value of the third float.
+ */ 
+  public Float3D(float vx, float vy, float vz)
+    {
+    super(vx,vy);
+    z = vz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (vx,vy,vz).
+ * 
+ * @param vx new value of the first float
+ * @param vy new value of the second float
+ * @param vz new value of the third float
+ */
+  public void set(int vx, int vy, int vz)
+    {
+    x = vx;
+    y = vy;
+    z = vz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (vx,vy,vz).
+ * 
+ * @param vx new value of the first float
+ * @param vy new value of the second float
+ * @param vz new value of the third float
+ */
+  public void set(float vx, float vy, float vz)
+    {
+    x = vx;
+    y = vy;
+    z = vz;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the third float contained.
+ * 
+ * @return The third float.
+ */
+  public float getZ()
+    {
+    return z;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// end of class   
+  }
diff --git a/src/main/java/org/distorted/library/type/Float4D.java b/src/main/java/org/distorted/library/type/Float4D.java
new file mode 100644
index 0000000..e093c03
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Float4D.java
@@ -0,0 +1,110 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A 4-dimensional data structure containing four floats. The floats have no particular meaning; 
+ * when this data structure is used in Interpolators, we can think of it as a 4-dimensional Point 
+ * a few of which the Interpolator interpolates between.
+ */
+
+public class Float4D extends Float3D 
+  {
+  float w;
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the four floats to (vx,vy,vz,vw).   
+ *   
+ * @param vx value of the first float.
+ * @param vy value of the second float.
+ * @param vz value of the third float.
+ * @param vw value of the fourth float.
+ */ 
+  public Float4D(int vx, int vy, int vz, int vw)
+    {
+    super(vx,vy,vz);
+    w = vw;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the four floats to (vx,vy,vz,vw).   
+ *   
+ * @param vx value of the first float.
+ * @param vy value of the second float.
+ * @param vz value of the third float.
+ * @param vw value of the fourth float.
+ */ 
+  public Float4D(float vx, float vy, float vz, float vw)
+    {
+    super(vx,vy,vz);
+    w = vw;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (vx,vy,vz,vw).
+ * 
+ * @param vx new value of the first float
+ * @param vy new value of the second float
+ * @param vz new value of the third float
+ * @param vw new value of the fourth float
+ */
+  public void set(int vx, int vy, int vz, int vw)
+    {
+    x = vx;
+    y = vy;
+    z = vz;
+    w = vw;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (vx,vy,vz,vw).
+ * 
+ * @param vx new value of the first float
+ * @param vy new value of the second float
+ * @param vz new value of the third float
+ * @param vw new value of the fourth float
+ */
+  public void set(float vx, float vy, float vz, float vw)
+    {
+    x = vx;
+    y = vy;
+    z = vz;
+    w = vw;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the fourth float contained.
+ * 
+ * @return The fourth float.
+ */
+  public float getW()
+    {
+    return w;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// end of class   
+  }
diff --git a/src/main/java/org/distorted/library/type/Interpolator.java b/src/main/java/org/distorted/library/type/Interpolator.java
new file mode 100644
index 0000000..cb26814
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Interpolator.java
@@ -0,0 +1,229 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Random;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** A class to interpolate between a List of Float{1,2,3,4}Ds.
+* <p><ul>
+* <li>if there is only one Point, just jump to it.
+* <li>if there are two Points, linearly bounce between them
+* <li>if there are more, interpolate a loop (or a path!) between them.
+* </ul>
+*/
+
+// The way Interpolation between more than 2 Points is done:
+// 
+// Def: let w[i] = (w[i](x), w[i](y), w[i](z)) be the direction and speed we have to be flying at Point P[i]
+//
+// time it takes to fly though one segment v[i] --> v[i+1] : 0.0 --> 1.0
+// w[i] should be parallel to v[i+1] - v[i-1]   (cyclic notation)
+// |w[i]| proportional to | P[i]-P[i+1] |
+//
+// Given that the flight route (X(t), Y(t), Z(t)) from P(i) to P(i+1)  (0<=t<=1) has to satisfy
+// X(0) = P[i  ](x), Y(0)=P[i  ](y), Z(0)=P[i  ](z), X'(0) = w[i  ](x), Y'(0) = w[i  ](y), Z'(0) = w[i  ](z)
+// X(1) = P[i+1](x), Y(1)=P[i+1](y), Z(1)=P[i+1](z), X'(1) = w[i+1](x), Y'(1) = w[i+1](y), Z'(1) = w[i+1](z)
+//
+// we have the solution:  X(t) = at^3 + bt^2 + ct + d where
+// a =  2*P[i](x) +   w[i](x) - 2*P[i+1](x) + w[i+1](x)
+// b = -3*P[i](x) - 2*w[i](x) + 3*P[i+1](x) - w[i+1](x)
+// c = w[i](x)<br>
+// d = P[i](x)
+//
+// and similarly Y(t) and Z(t).
+
+public abstract class Interpolator 
+  {
+  /**
+   * One revolution takes us from the first vector to the last and back to first through the shortest path. 
+   */
+  public static final int MODE_LOOP = 0; 
+  /**
+   * We come back from the last to the first vector through the same way we got there.
+   */
+  public static final int MODE_PATH = 1; 
+  /**
+   * We just jump back from the last point to the first.
+   */
+  public static final int MODE_JUMP = 2; 
+ 
+  protected static Random mRnd = new Random();
+  
+  protected static final int NUM_NOISE = 5; // used iff mNoise>0.0. Number of intermediary points between each pair of adjacent vectors
+                                            // where we randomize noise factors to make the way between the two vectors not so smooth.
+  protected int numPoints;
+  protected int mVecCurr;    
+  protected boolean cacheDirty; // VectorCache not up to date
+  protected int mMode;          // LOOP, PATH or JUMP
+  protected long mDuration;     // number of miliseconds it takes to do a full loop/path from first vector to the last and back to the first 
+  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. 
+  protected float mNoise;       // how 'smooth' our path form each vector to the next is. mNoise = 0.0 (min) --> completely smooth; mNoise==1.0 (max) --> very uneven
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// hide this from Javadoc
+  
+  Interpolator()
+    {
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  public void interpolateMain(float[] buffer, int offset, long currentDuration)
+    {
+    if( mDuration<=0.0f ) 
+      {
+      interpolate(buffer,offset,mCount-(int)mCount);  
+      }
+    else
+      {
+      float x = (float)currentDuration/mDuration;
+           
+      if( x<=mCount || mCount<=0.0f )
+        {
+        interpolate(buffer,offset,x-(int)x);
+        }
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean interpolateMain(float[] buffer, int offset, long currentDuration, long step)
+    {
+    if( mDuration<=0.0f ) 
+      {
+      interpolate(buffer,offset,mCount-(int)mCount);
+      return false;
+      }
+     
+    float x = (float)currentDuration/mDuration;
+           
+    if( x<=mCount || mCount<=0.0f )
+      {
+      interpolate(buffer,offset,x-(int)x);
+        
+      if( currentDuration+step > mDuration*mCount && mCount>0.0f )
+        {
+        interpolate(buffer,offset,mCount-(int)mCount);
+        return true;
+        }
+      }
+    
+    return false;
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// internal debugging only!
+  
+  public String print()
+    {
+    return "duration="+mDuration+" count="+mCount+" Noise="+mNoise+" numVectors="+numPoints+" mMode="+mMode;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  abstract void interpolate(float[] buffer, int offset, float time);
+  abstract void createNoise();
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the mode of the interpolation to Loop, Path or Jump.
+ * <ul>
+ * <li>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.
+ * <li>Path is when we come back from the last point back to the first the same way we got there.
+ * <li>Jump is when we go from first to last and then jump back to the first.
+ * </ul>
+ * 
+ * @param mode {@link Interpolator#MODE_LOOP}, {@link Interpolator#MODE_PATH} or {@link Interpolator#MODE_JUMP}.
+ */
+
+  public void setMode(int mode)
+    {
+    mMode = mode;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the number of Float{1,2,3,4}Ds this Interpolator has been fed with.
+ *   
+ * @return the number of Float{1,2,3,4}Ds we are currently interpolating through.
+ */
+  public synchronized int getNumPoints()
+    {
+    return numPoints;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Controls how many times we want to interpolate.
+ * <p>
+ * Count equal to 1 means 'go from the first Float{1,2,3,4}D to the last and back'. Does not have to be an
+ * integer - i.e. count=1.5 would mean 'start at the first Point, go to the last, come back to the first, 
+ * go to the last again and stop'.
+ * Count<=0 means 'go on interpolating indefinitely'.
+ * 
+ * @param count the number of times we want to interpolate between our collection of Float{1,2,3,4}Ds.
+ */
+  public void setCount(float count)
+    {
+    mCount = count;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the time it takes to do one full interpolation.
+ * 
+ * @param duration Time, in milliseconds, it takes to do one full interpolation, i.e. go from the first 
+ *                 Point to the last and back. 
+ */
+  
+  public void setDuration(long duration)
+    {
+    mDuration = duration;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the 'smoothness' of interpolation. 
+ * <p>
+ * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible. 
+ * Increasing noise makes the Interpolator increasingly deviate from this path, pseudo-randomly speeding 
+ * up and slowing down, etc.
+ * 
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+  
+  public void setNoise(float noise)
+    {
+    if( mNoise==0.0f && noise != 0.0f )  
+      createNoise();
+   
+    if( mNoise<0.0f ) mNoise = 0.0f;
+    if( mNoise>1.0f ) mNoise = 1.0f;
+   
+    mNoise = noise;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// end of DistortedInterpolator
+  }
diff --git a/src/main/java/org/distorted/library/type/Interpolator1D.java b/src/main/java/org/distorted/library/type/Interpolator1D.java
new file mode 100644
index 0000000..ae4ed62
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Interpolator1D.java
@@ -0,0 +1,499 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 1-dimensional implementation of the Interpolator class to interpolate between a list 
+* of Float1Ds.
+*/
+
+public class Interpolator1D extends Interpolator 
+  {
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 class VectorNoise
+    {
+    float[] nx;
+   
+    public VectorNoise()
+      {
+      nx = new float[NUM_NOISE]; 
+      nx[0] = mRnd.nextFloat();
+      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
+      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
+      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
+      }
+    }
+  
+  private Vector<VectorCache> vc;
+  private VectorCache tmp1, tmp2;
+ 
+  private Vector<Float1D> vv;
+  private Float1D prev, curr, next;
+ 
+  private Vector<VectorNoise> vn;
+  private VectorNoise tmpN;
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void createNoise()
+    {
+    if( vn==null )
+      {
+      vn = new Vector<VectorNoise>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void vec(int c)
+    {
+    int p = c>0 ? c-1: numPoints-1;
+    int n = c<numPoints-1 ? c+1: 0;
+    
+    prev = vv.elementAt(p);
+    curr = vv.elementAt(c);
+    next = vv.elementAt(n);
+
+    tmp1 = vc.elementAt(c);
+    
+    float px = curr.x - prev.x;
+    float nx = next.x - curr.x;
+     
+    float d = nx*nx;
+    
+    if( d>0 )
+      {
+      float q = (float)Math.sqrt((px*px)/d);
+      
+      if( q>1 )
+        {
+        tmp1.x = nx+px/q;
+        }
+      else
+        {
+        tmp1.x = px+nx*q;
+        }
+      }
+    else
+      {
+      tmp1.x = 0.0f;
+      }
+    }
+      
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmp1= vc.elementAt(0);
+      curr= vv.elementAt(0);
+        
+      tmp1.ax = 0.0f;
+      tmp1.bx = 0.0f;
+      tmp1.cx = curr.x;
+      tmp1.dx = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmp1= vc.elementAt(0);
+      tmp2= vc.elementAt(1);
+      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;
+      
+      tmp2.ax = 0.0f;
+      tmp2.bx = 0.0f;
+      tmp2.cx = curr.x - next.x;
+      tmp2.dx = next.x;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) vec(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmp1= vc.elementAt(i);
+        tmp2= vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+    
+        tmp1.vx = 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;
+        }
+      }
+   
+    cacheDirty = false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float noise(float time,int vecNum)
+    {
+    float lower, upper, len;  
+    float d = time*(NUM_NOISE+1);
+    int index = (int)d;
+    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
+    tmpN = vn.elementAt(vecNum);
+   
+    if( index==0 )
+      {
+      len = 1.0f/(NUM_NOISE+1);  
+      return (len + mNoise*(tmpN.nx[0]-len))*d;
+      }
+    if( index==NUM_NOISE )
+      {
+      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
+      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);   
+      return (1.0f-lower)*(d-NUM_NOISE) + lower;   
+      }
+   
+    len = ((float)index)/(NUM_NOISE+1);
+    lower = len + mNoise*(tmpN.nx[index-1]-len);   
+    len = ((float)index+1)/(NUM_NOISE+1); 
+    upper = len + mNoise*(tmpN.nx[index  ]-len);
+            
+    return (upper-lower)*(d-index) + lower; 
+    }
+   
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Interpolator1D()
+    {
+    vv = new Vector<Float1D>();
+    vc = new Vector<VectorCache>();
+    vn = null;
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = 0;
+    mCount = 0.5f;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Float1D. 
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Float1D, if 0<=location&lt;getNumPoints(), or null otherwise. 
+ */
+  public synchronized Float1D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * 
+ * @param location the index of the Point we are setting.
+ * @param x New value of its first float.
+ */
+  public synchronized void setPoint(int location, float x)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x);
+        cacheDirty=true;
+        }
+      }
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float1D to the end of our list of Points to interpolate through.
+ * <p>   
+ * Only a reference to the Point gets added to the List; this means that one can add a Point 
+ * here, and later on {@link Float1D#set(float)} it to some new value and the change will
+ * be seamlessly reflected in the interpolated path.  
+ * <p>
+ * A Point can be added multiple times.
+ *   
+ * @param v The Point to add.
+ */
+  public synchronized void add(Float1D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+     
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0: 
+        case 1: break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                cacheDirty = true;
+                break;
+        default:vc.add(new VectorCache());
+                cacheDirty = true;
+        }
+     
+      numPoints++;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float1D to the location'th place in our List of Points to interpolate through.  
+ *   
+ * @param location Index in our List to add the new Point at.
+ * @param v The Point to add.
+ */
+  public synchronized void add(int location, Float1D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+             
+      switch(numPoints)
+        {
+        case 0:
+        case 1: break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                cacheDirty = true;
+                break;
+        default:vc.add(location,new VectorCache());
+                cacheDirty = true;
+        }
+      
+      numPoints++;
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all occurrences of Point v from the List of Points to interpolate through.  
+ * 
+ * @param v The Point to remove.
+ * @return <code>true</code> if we have removed at least one Point.
+ */
+  public synchronized boolean remove(Float1D v)
+    {
+    int n = vv.indexOf(v);
+    boolean found = false;
+   
+    while( n>=0 ) 
+      {
+      vv.remove(n);
+     
+      if( vn!=null ) vn.remove(0);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1:
+        case 2: break;
+        case 3: vc.removeAllElements();
+                break;
+        default:vc.remove(n);
+                cacheDirty=true;
+        }
+
+      numPoints--;
+      found = true;
+      n = vv.indexOf(v);
+      }
+   
+    return found;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes a location'th Point from the List of Points we interpolate through.
+ * 
+ * @param location index of the Point we want to remove. 
+ * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
+ */
+  public synchronized boolean remove(int location)
+    {
+    if( location>=0 && location<numPoints ) 
+      {
+      vv.removeElementAt(location);
+      
+      if( vn!=null ) vn.remove(0);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                break;
+        default:vc.removeElementAt(location);
+        }
+
+      numPoints--;
+      cacheDirty = true; 
+      return true;
+      }
+
+   return false;
+   }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all Points.
+ */
+  public synchronized void removeAll()
+    {
+    numPoints = 0;
+    vv.removeAllElements();
+    vc.removeAllElements();
+    cacheDirty = false;
+   
+    if( vn!=null ) vn.removeAllElements();
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * <p>
+ * Since this is a 1-dimensional Interpolator, the resulting interpolated Float1D gets written
+ * to a single location in the buffer: buffer[offset]. 
+ * 
+ * @param buffer Float buffer we will write the resulting Float1D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
+ *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
+ */
+  public synchronized void interpolate(float[] buffer, int offset, float time)
+    {
+    switch(numPoints)
+      {
+      case 0: buffer[offset] = 0.0f;
+              break;
+      case 1: curr = vv.elementAt(0);
+              buffer[offset] = curr.x;
+              break;
+      case 2: curr = vv.elementAt(0);
+              next = vv.elementAt(1);
+             
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+             
+              if( vn!=null )
+                {
+                time = noise(time,0);
+                }
+             
+              buffer[offset] = (next.x-curr.x)*time + curr.x;
+              break;
+      default:float t = time;
+            
+              switch(mMode)
+                {
+                case MODE_LOOP: time = time*numPoints;
+                                break;
+                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
+                                break;
+                case MODE_JUMP: time = time*(numPoints-1);
+                                break;
+                }
+      
+              int vecCurr = (int)time;
+              time = time-vecCurr;
+      
+              if( vecCurr>=0 && vecCurr<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mVecCurr!= vecCurr )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext;   
+                  mVecCurr = vecCurr;
+                                
+                  switch(mMode)
+                    {
+                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
+                                    break;
+                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
+                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
+                                    break;
+                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
+                                    break;
+                    default       : vecNext = 0;                
+                    }
+              
+                  next = vv.elementAt(vecNext);
+                  tmp2 = vc.elementAt(vecNext);
+              
+                  if( tmp2.vx!=next.x ) recomputeCache();
+                  }
+             
+                if( vn!=null )
+                  {
+                  time = noise(time,vecCurr);
+                  }
+            
+                tmp1 = vc.elementAt(vecCurr);
+                buffer[offset] = ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx;
+                break;
+                }
+        }
+     }  
+  
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Interpolator2D.java b/src/main/java/org/distorted/library/type/Interpolator2D.java
new file mode 100644
index 0000000..df8ddb6
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Interpolator2D.java
@@ -0,0 +1,551 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 2-dimensional implementation of the Interpolator class to interpolate between a list 
+* of Float2Ds.
+*/
+
+public class Interpolator2D extends Interpolator 
+  {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 class VectorNoise
+    {    
+    float[] nx;
+    float[] ny;
+   
+    public VectorNoise()
+      {
+      nx = new float[NUM_NOISE]; 
+      nx[0] = mRnd.nextFloat();
+      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
+      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
+      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
+     
+      ny = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
+      }
+    }
+    
+  private Vector<VectorCache> vc;
+  private VectorCache tmp1, tmp2;
+   
+  private Vector<Float2D> vv;
+  private Float2D prev, curr, next;
+ 
+  private Vector<VectorNoise> vn;
+  private VectorNoise tmpN;
+  
+  private float mFactor;
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void createNoise()
+    {
+    if( vn==null )
+      {
+      vn = new Vector<VectorNoise>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+      }
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void vec(int c)
+    {
+    int p = c>0 ? c-1: numPoints-1;
+    int n = c<numPoints-1 ? c+1: 0;
+    
+    prev = vv.elementAt(p);
+    curr = vv.elementAt(c);
+    next = vv.elementAt(n);
+
+    tmp1 = vc.elementAt(c);
+    
+    float px = curr.x - prev.x;
+    float py = curr.y - prev.y;
+    float nx = next.x - curr.x;
+    float ny = next.y - curr.y;
+     
+    float d = nx*nx+ny*ny;
+    
+    if( d>0 )
+      {
+      float q = (float)Math.sqrt((px*px+py*py)/d);
+      
+      if( q>1 )
+        {
+        tmp1.x = nx+px/q;
+        tmp1.y = ny+py/q;
+        }
+      else
+        {
+        tmp1.x = px+nx*q;
+        tmp1.y = py+ny*q;
+        }
+      }
+    else
+      {
+      tmp1.x = 0.0f;
+      tmp1.y = 0.0f;
+      }
+    }
+   
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      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;
+      }
+    else if( numPoints==2 )
+      {
+      tmp1= vc.elementAt(0);
+      tmp2= vc.elementAt(1);
+      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;
+      
+      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;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) vec(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmp1= vc.elementAt(i);
+        tmp2= vc.elementAt(n);
+        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;
+        }
+      }
+    
+    cacheDirty = false;
+    }
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float noise(float time,int vecNum)
+    {
+    float lower, upper, len;  
+    float d = time*(NUM_NOISE+1);
+    int index = (int)d;
+    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
+    tmpN = vn.elementAt(vecNum);
+   
+    float x = d-index;
+    x = x*x*(3-2*x);
+   
+    switch(index)
+      {
+      case 0        : mFactor = mNoise*tmpN.ny[0]*x;  
+                      return time + mNoise*(d*tmpN.nx[0]-time);                
+      case NUM_NOISE: mFactor= mNoise*tmpN.ny[NUM_NOISE-1]*(1-x);
+                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
+                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
+      default       : float yb = tmpN.ny[index  ];
+                      float ya = tmpN.ny[index-1];
+                      mFactor  = mNoise*((yb-ya)*x+ya);
+   
+                      len = ((float)index)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
+                      len = ((float)index+1)/(NUM_NOISE+1); 
+                      upper = len + mNoise*(tmpN.nx[index  ]-len);
+            
+                      return (upper-lower)*(d-index) + lower; 
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Interpolator2D()
+    {
+    vv = new Vector<Float2D>();
+    vc = new Vector<VectorCache>();
+    vn = null;
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = 0;
+    mCount = 0.5f;
+    mNoise = 0.0f;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Float2D. 
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Float2D, if 0<=location&lt;getNumPoints(), or null otherwise. 
+ */  
+  public synchronized Float2D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * 
+ * @param location the index of the Point we are setting.
+ * @param x New value of its first float.
+ */
+  public synchronized void setPoint(int location, float x, float y)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x,y);
+        cacheDirty=true;
+        }
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float2D to the end of our list of Points to interpolate through.
+ * <p>   
+ * Only a reference to the Point gets added to the List; this means that one can add a Point 
+ * here, and later on {@link Float2D#set(float,float)} it to some new value and the change 
+ * will be seamlessly reflected in the interpolated path.  
+ * <p>
+ * A Point can be added multiple times.
+ *   
+ * @param v The Point to add.
+ */  
+  public synchronized void add(Float2D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+     
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0:
+        case 1: break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(new VectorCache());
+        }
+     
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float2D to the location'th place in our List of Points to interpolate through.  
+ *   
+ * @param location Index in our List to add the new Point at.
+ * @param v The Point to add.
+ */  
+  public synchronized void add(int location, Float2D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0:
+        case 1: break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(location,new VectorCache());
+        }
+      
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all occurrences of Point v from the List of Points to interpolate through.  
+ * 
+ * @param v The Point to remove.
+ * @return <code>true</code> if we have removed at least one Point.
+ */
+  public synchronized boolean remove(Float2D v)
+    {
+    int n = vv.indexOf(v);
+    boolean found = false;
+   
+    while( n>=0 ) 
+      {
+      vv.remove(n);
+     
+      if( vn!=null ) vn.remove(0);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                break;
+        default:vc.remove(n);
+        }
+     
+      numPoints--;
+      found = true;
+      n = vv.indexOf(v);
+      }
+   
+    if( found ) 
+      {
+      cacheDirty=true;
+      }
+   
+    return found;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes a location'th Point from the List of Points we interpolate through.
+ * 
+ * @param location index of the Point we want to remove. 
+ * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
+ */
+  public synchronized boolean remove(int location)
+    {
+    if( location>=0 && location<numPoints ) 
+      {
+      vv.removeElementAt(location);
+      
+      if( vn!=null ) vn.remove(0);
+      
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                break;
+        default:vc.removeElementAt(location);
+        }
+
+      numPoints--;
+      cacheDirty = true; 
+      return true;
+      }
+
+   return false;
+   }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all Points.
+ */
+  public synchronized void removeAll()
+    {
+    numPoints = 0;
+    vv.removeAllElements();
+    vc.removeAllElements();
+    cacheDirty = false;
+   
+    if( vn!=null ) vn.removeAllElements();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * <p>
+ * Since this is a 2-dimensional Interpolator, the resulting interpolated Float2D gets written
+ * to two locations in the buffer: buffer[offset] and buffer[offset+1]. 
+ * 
+ * @param buffer Float buffer we will write the resulting Float2D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
+ *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
+ */  
+  public synchronized void interpolate(float[] buffer, int offset, float time)
+    {
+    switch(numPoints)
+      {
+      case 0: buffer[offset  ] = 0.0f;
+              buffer[offset+1] = 0.0f;
+              break;
+      case 1: curr = vv.elementAt(0);
+              buffer[offset  ] = curr.x;
+              buffer[offset+1] = curr.y;
+              break;
+      case 2: curr = vv.elementAt(0);
+              next = vv.elementAt(1);
+               
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+             
+              if( vn!=null )
+                {
+                time = noise(time,0);
+              
+                float dx2 = next.x-curr.x;
+                float dy2 = next.y-curr.y;
+   
+                buffer[offset  ] = dx2*time + curr.x +dy2*mFactor;
+                buffer[offset+1] = dy2*time + curr.y -dx2*mFactor;
+                }
+              else
+                {
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
+                }
+              
+              break;
+      default:float t = time;
+        
+              switch(mMode)
+                {
+                case MODE_LOOP: time = time*numPoints;
+                                break;
+                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
+                                break;
+                case MODE_JUMP: time = time*(numPoints-1);
+                                break;
+                }
+            
+              int vecCurr = (int)time;
+              time = time-vecCurr;
+      
+              if( vecCurr>=0 && vecCurr<numPoints )
+                { 
+                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
+                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext;   
+                  mVecCurr = vecCurr;
+                                
+                  switch(mMode)
+                    {
+                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
+                                    break;
+                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
+                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
+                                    break;
+                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
+                                    break;
+                    default       : vecNext = 0;                
+                    }
+              
+                  next = vv.elementAt(vecNext);
+                  tmp2 = vc.elementAt(vecNext);
+              
+                  if( tmp2.vx!=next.x || tmp2.vy!=next.y ) recomputeCache();
+                  }
+              
+                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;
+                 
+                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx +dy2*mFactor;
+                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy -dx2*mFactor;
+                  } 
+                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;
+                  }
+                
+                break;
+                }
+      }
+    }  
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Interpolator3D.java b/src/main/java/org/distorted/library/type/Interpolator3D.java
new file mode 100644
index 0000000..4fa1286
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Interpolator3D.java
@@ -0,0 +1,669 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 3-dimensional implementation of the Interpolator class to interpolate between a list 
+* of Float3Ds.
+*/
+
+public class Interpolator3D extends Interpolator 
+  {
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 class VectorNoise
+    {
+    float[] nx;
+    float[] ny;
+    float[] nz;
+   
+    public VectorNoise()
+      {
+      nx = new float[NUM_NOISE]; 
+      nx[0] = mRnd.nextFloat();
+      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1]+mRnd.nextFloat();
+      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
+      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
+     
+      ny = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
+     
+      nz = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) nz[i] = mRnd.nextFloat()-0.5f;  
+      }
+    }
+  
+  private Vector<VectorCache> vc;
+  private VectorCache tmp1, tmp2;
+
+  private Vector<Float3D> vv;
+  private Float3D prev, curr, next;
+  
+  private Vector<VectorNoise> vn;
+  private VectorNoise tmpN;
+  
+  private float mFactor1, mFactor2;  // used in Noise only. Those are noise factors; 1=noise of the (vec1X,vec1Y,vec1Z) vector; 2=noise of (vec2X,vec2Y,vec2Z)
+  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.
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void createNoise()
+    {
+    if( vn==null )
+      {  
+      vn = new Vector<VectorNoise>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+      }
+    }
+   
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void vec(int c)
+    {
+    int p = c>0 ? c-1: numPoints-1;
+    int n = c<numPoints-1 ? c+1: 0;
+    
+    prev = vv.elementAt(p);
+    curr = vv.elementAt(c);
+    next = vv.elementAt(n);
+
+    tmp1 = vc.elementAt(c);
+    
+    float px = curr.x - prev.x;
+    float py = curr.y - prev.y;
+    float pz = curr.z - prev.z;
+    float nx = next.x - curr.x;
+    float ny = next.y - curr.y;
+    float nz = next.z - curr.z;
+     
+    float d = nx*nx+ny*ny+nz*nz;
+    
+    if( d>0 )
+      {
+      float q = (float)Math.sqrt((px*px+py*py+pz*pz)/d);
+      
+      if( q>1 )
+        {
+        tmp1.x = nx+px/q;
+        tmp1.y = ny+py/q;
+        tmp1.z = nz+pz/q;
+        }
+      else
+        {
+        tmp1.x = px+nx*q;
+        tmp1.y = py+ny*q;
+        tmp1.z = pz+nz*q;
+        }
+      }
+    else
+      {
+      tmp1.x = 0.0f;
+      tmp1.y = 0.0f;
+      tmp1.z = 0.0f;  
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      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;
+      }
+    else if( numPoints==2 )
+      {
+      tmp1= vc.elementAt(0);
+      tmp2= vc.elementAt(1);
+      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;
+      
+      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;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) vec(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmp1= vc.elementAt(i);
+        tmp2= vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+      
+        tmp1.vx = curr.x;
+        tmp1.vy = curr.y;
+        tmp1.vz = 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.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.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;
+        }
+      }
+   
+    cacheDirty = false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float noise(float time,int vecNum)
+    {
+    float lower, upper, len;  
+    float d = time*(NUM_NOISE+1);
+    int index = (int)d;
+    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
+    tmpN = vn.elementAt(vecNum);
+   
+    float t = d-index;
+    t = t*t*(3-2*t);
+   
+    switch(index)
+      {
+      case 0        : mFactor1 = mNoise*tmpN.ny[0]*t;
+                      mFactor2 = mNoise*tmpN.nz[0]*t;
+                      return time + mNoise*(d*tmpN.nx[0]-time);
+      case NUM_NOISE: mFactor1= mNoise*tmpN.ny[NUM_NOISE-1]*(1-t);
+                      mFactor2= mNoise*tmpN.nz[NUM_NOISE-1]*(1-t);
+                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
+                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
+      default       : float ya,yb;
+                      yb = tmpN.ny[index  ];
+                      ya = tmpN.ny[index-1];
+                      mFactor1 = mNoise*((yb-ya)*t+ya);
+                      yb = tmpN.nz[index  ];
+                      ya = tmpN.nz[index-1];
+                      mFactor2 = mNoise*((yb-ya)*t+ya);
+   
+                      len = ((float)index)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
+                      len = ((float)index+1)/(NUM_NOISE+1); 
+                      upper = len + mNoise*(tmpN.nx[index  ]-len);
+            
+                      return (upper-lower)*(d-index) + lower; 
+      }
+    }
+     
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Interpolator3D()
+    {
+    vv = new Vector<Float3D>();
+    vc = new Vector<VectorCache>();
+    vn = null;
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = 0;
+    mCount = 0.5f;
+    mNoise = 0.0f;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Float3D. 
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Float3D, if 0<=location&lt;getNumPoints(), or null otherwise. 
+ */  
+  public synchronized Float3D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * 
+ * @param location the index of the Point we are setting.
+ * @param x New value of its first float.
+ */
+  public synchronized void setPoint(int location, float x, float y, float z)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x,y,z);
+        cacheDirty=true;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float3D to the end of our list of Points to interpolate through.
+ * <p>   
+ * Only a reference to the Point gets added to the List; this means that one can add a Point 
+ * here, and later on {@link Float3D#set(float,float,float)} it to some new value and the 
+ * change will be seamlessly reflected in the interpolated path.  
+ * <p>
+ * A Point can be added multiple times.
+ *   
+ * @param v The Point to add.
+ */    
+  public synchronized void add(Float3D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+        
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: setUpVectors(0.0f,null);
+                break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(new VectorCache());
+        }
+
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float3D to the location'th place in our List of Points to interpolate through.  
+ *   
+ * @param location Index in our List to add the new Point at.
+ * @param v The Point to add.
+ */  
+  public synchronized void add(int location, Float3D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: setUpVectors(0.0f,null);
+                break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(location,new VectorCache());
+        }
+
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all occurrences of Point v from the List of Points to interpolate through.  
+ * 
+ * @param v The Point to remove.
+ * @return <code>true</code> if we have removed at least one Point.
+ */
+  public synchronized boolean remove(Float3D v)
+    {
+    int n = vv.indexOf(v);
+    boolean found = false;
+   
+    while( n>=0 ) 
+      {
+      vv.remove(n);
+     
+      if( vn!=null ) vn.remove(0);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                setUpVectors(0.0f,null);
+                break;
+        default:vc.remove(n);
+        }
+
+      numPoints--;
+      found = true;
+      n = vv.indexOf(v);
+      }
+   
+    if( found ) 
+      {
+      cacheDirty=true;
+      }
+   
+    return found;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes a location'th Point from the List of Points we interpolate through.
+ * 
+ * @param location index of the Point we want to remove. 
+ * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
+ */
+  public synchronized boolean remove(int location)
+    {
+    if( location>=0 && location<numPoints ) 
+      {
+      vv.removeElementAt(location);
+       
+      if( vn!=null ) vn.remove(0);
+      
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                setUpVectors(0.0f,null);
+                break;
+        default:vc.removeElementAt(location);
+        }
+
+      numPoints--;
+      cacheDirty = true; 
+      return true;
+      }
+
+   return false;
+   }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all Points.
+ */
+  public synchronized void removeAll()
+    {
+    numPoints = 0;
+    vv.removeAllElements();
+    vc.removeAllElements();
+    cacheDirty = false;
+   
+    if( vn!=null ) vn.removeAllElements();
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * <p>
+ * Since this is a 3-dimensional Interpolator, the resulting interpolated Float3D gets written
+ * to three locations in the buffer: buffer[offset], buffer[offset+1] and buffer[offset+2]. 
+ * 
+ * @param buffer Float buffer we will write the resulting Float3D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
+ *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
+ */    
+  public synchronized void interpolate(float[] buffer, int offset, float time)
+    {  
+    switch(numPoints)
+      {
+      case 0: buffer[offset  ] = 0.0f;
+              buffer[offset+1] = 0.0f;
+              buffer[offset+2] = 0.0f;
+              break;
+      case 1: curr = vv.elementAt(0);
+              buffer[offset  ] = curr.x;
+              buffer[offset+1] = curr.y;
+              buffer[offset+2] = curr.z;
+              break;
+      case 2: curr = vv.elementAt(0);
+              next = vv.elementAt(1);
+             
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+             
+              if( vn!=null )
+                {
+                time = noise(time,0);
+            
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor1 + vec2X*mFactor2);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor1 + vec2Y*mFactor2);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor1 + vec2Z*mFactor2); 
+                }
+              else
+                {
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
+                }
+             
+              break;
+      default:float t = time;
+        
+              switch(mMode)
+                {
+                case MODE_LOOP: time = time*numPoints;
+                                break;
+                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
+                                break;
+                case MODE_JUMP: time = time*(numPoints-1);
+                                break;
+                }
+           
+              int vecCurr = (int)time;
+              time = time-vecCurr;
+      
+              if( vecCurr>=0 && vecCurr<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
+                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext;   
+                  mVecCurr = vecCurr;
+                       
+                  switch(mMode)
+                    {
+                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
+                                    break;
+                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
+                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
+                                    break;
+                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
+                                    break;
+                    default       : vecNext = 0;                
+                    }
+              
+                  next = vv.elementAt(vecNext);
+                  tmp2 = vc.elementAt(vecNext);
+              
+                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z ) recomputeCache();
+                  }
+            
+                tmp1 = vc.elementAt(vecCurr);
+               
+                if( vn!=null )
+                  {
+                  time = noise(time,vecCurr);
+              
+                  setUpVectors(time,tmp1);
+                 
+                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor1 + vec2X*mFactor2);
+                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor1 + vec2Y*mFactor2);
+                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor1 + vec2Z*mFactor2);
+                  }
+                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;
+                  }
+               
+                break;
+                }
+       }
+     }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Interpolator4D.java b/src/main/java/org/distorted/library/type/Interpolator4D.java
new file mode 100644
index 0000000..72aa1e3
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Interpolator4D.java
@@ -0,0 +1,771 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 4-dimensional implementation of the Interpolator class to interpolate between a list 
+* of Float4Ds.
+*/
+
+public class Interpolator4D extends Interpolator 
+  {
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 class VectorNoise
+    {
+    float[] nx;
+    float[] ny;
+    float[] nz;
+    float[] nw;
+   
+    public VectorNoise()
+      {
+      nx = new float[NUM_NOISE]; 
+      nx[0] = mRnd.nextFloat();
+      for(int i=1; i<NUM_NOISE; i++) nx[i] = nx[i-1] + mRnd.nextFloat();
+      float sum = nx[NUM_NOISE-1] + mRnd.nextFloat();
+      for(int i=0; i<NUM_NOISE; i++) nx[i] /=sum;
+     
+      ny = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) ny[i] = mRnd.nextFloat()-0.5f;
+     
+      nz = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) nz[i] = mRnd.nextFloat()-0.5f;
+     
+      nw = new float[NUM_NOISE];
+      for(int i=0; i<NUM_NOISE; i++) nw[i] = mRnd.nextFloat()-0.5f;  
+      }
+    }
+  
+  private Vector<VectorCache> vc;
+  private VectorCache tmp1, tmp2;
+
+  private Vector<Float4D> vv;
+  private Float4D prev, curr, next;
+  
+  private Vector<VectorNoise> vn;
+  private VectorNoise tmpN;
+  
+  private float mFactor1, mFactor2, mFactor3; // used in Noise only. Those are noise factors; 1=noise of the (vec1X,vec1Y,vec1Z,vec1W) vector; 2=noise of (vec2X,vec2Y,vec2Z,vec2W) and same for vec3.
+  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.
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void createNoise()
+    {
+    if( vn==null )
+      {  
+      vn = new Vector<VectorNoise>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+      }
+    }
+   
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void vec(int c)
+    {
+    int p = c>0 ? c-1: numPoints-1;
+    int n = c<numPoints-1 ? c+1: 0;
+    
+    prev = vv.elementAt(p);
+    curr = vv.elementAt(c);
+    next = vv.elementAt(n);
+
+    tmp1 = vc.elementAt(c);
+    
+    float px = curr.x - prev.x;
+    float py = curr.y - prev.y;
+    float pz = curr.z - prev.z;
+    float pw = curr.w - prev.w;
+    float nx = next.x - curr.x;
+    float ny = next.y - curr.y;
+    float nz = next.z - curr.z;
+    float nw = next.w - curr.w;
+     
+    float d = nx*nx+ny*ny+nz*nz+nw*nw;
+    
+    if( d>0 )
+      {
+      float q = (float)Math.sqrt((px*px+py*py+pz*pz+pw*pw)/d);
+      
+      if( q>1 )
+        {
+        tmp1.x = nx+px/q;
+        tmp1.y = ny+py/q;
+        tmp1.z = nz+pz/q;
+        tmp1.w = nw+pw/q;
+        }
+      else
+        {
+        tmp1.x = px+nx*q;
+        tmp1.y = py+ny*q;
+        tmp1.z = pz+nz*q;
+        tmp1.w = pw+nw*q;
+        }
+      }
+    else
+      {
+      tmp1.x = 0.0f;
+      tmp1.y = 0.0f;
+      tmp1.z = 0.0f;  
+      tmp1.w = 0.0f;
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      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;
+      }
+    else if( numPoints==2 )
+      {
+      tmp1= vc.elementAt(0);
+      tmp2= vc.elementAt(1);
+      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;
+      
+      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;
+      }
+    else
+      {
+      int i, n;  
+      
+      for(i=0; i<numPoints; i++) vec(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmp1= vc.elementAt(i);
+        tmp2= vc.elementAt(n);
+        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.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.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.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;
+        }
+      }
+   
+    cacheDirty = false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float noise(float time,int vecNum)
+    {
+    float lower, upper, len;  
+    float d = time*(NUM_NOISE+1);
+    int index = (int)d;
+    if( index>=NUM_NOISE+1 ) index=NUM_NOISE;
+    tmpN = vn.elementAt(vecNum);
+   
+    float t = d-index;
+    t = t*t*(3-2*t);
+   
+    switch(index)
+      {
+      case 0        : mFactor1 = mNoise*tmpN.ny[0]*t;
+                      mFactor2 = mNoise*tmpN.nz[0]*t;
+                      mFactor3 = mNoise*tmpN.nw[0]*t;
+                      return time + mNoise*(d*tmpN.nx[0]-time);
+      case NUM_NOISE: mFactor1= mNoise*tmpN.ny[NUM_NOISE-1]*(1-t);
+                      mFactor2= mNoise*tmpN.nz[NUM_NOISE-1]*(1-t);
+                      mFactor3= mNoise*tmpN.nw[NUM_NOISE-1]*(1-t);
+                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[NUM_NOISE-1]-len);  
+                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
+      default       : float ya,yb;
+                      yb = tmpN.ny[index  ];
+                      ya = tmpN.ny[index-1];
+                      mFactor1 = mNoise*((yb-ya)*t+ya);
+                      yb = tmpN.nz[index  ];
+                      ya = tmpN.nz[index-1];
+                      mFactor2 = mNoise*((yb-ya)*t+ya);
+                      yb = tmpN.nw[index  ];
+                      ya = tmpN.nw[index-1];
+                      mFactor3 = mNoise*((yb-ya)*t+ya);
+   
+                      len = ((float)index)/(NUM_NOISE+1);
+                      lower = len + mNoise*(tmpN.nx[index-1]-len);   
+                      len = ((float)index+1)/(NUM_NOISE+1); 
+                      upper = len + mNoise*(tmpN.nx[index  ]-len);
+            
+                      return (upper-lower)*(d-index) + lower; 
+      }
+    }
+     
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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*vw)/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;
+      vec2X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
+      vec2Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
+      vec2Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
+      vec2W = 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;
+        vec2X = 0.0f - coeff31*vx - coeff32*vec1X - coeff33*vec2X;
+        vec2Y = 0.0f - coeff31*vy - coeff32*vec1Y - coeff33*vec2Y;
+        vec2Z = 0.0f - coeff31*vz - coeff32*vec1Z - coeff33*vec2Z;
+        vec2W = 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
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Interpolator4D()
+    {
+    vv = new Vector<Float4D>();
+    vc = new Vector<VectorCache>();
+    vn = null;
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = 0;
+    mCount = 0.5f;
+    mNoise = 0.0f;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Float4D. 
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Float4D, if 0<=location&lt;getNumPoints(), or null otherwise. 
+ */  
+  public synchronized Float4D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * 
+ * @param location the index of the Point we are setting.
+ * @param x New value of its first float.
+ */
+  public synchronized void setPoint(int location, float x, float y, float z, float w)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x,y,z,w);
+        cacheDirty=true;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float4D to the end of our list of Points to interpolate through.
+ * <p>   
+ * Only a reference to the Point gets added to the List; this means that one can add a Point 
+ * here, and later on {@link Float4D#set(float,float,float,float)} it to some new value and 
+ * the change will be seamlessly reflected in the interpolated path.  
+ * <p>
+ * A Point can be added multiple times.
+ *   
+ * @param v The Point to add.
+ */    
+  public synchronized void add(Float4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+        
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+       switch(numPoints)
+         {
+         case 0: break;
+         case 1: setUpVectors(0.0f,null);
+                 break;
+         case 2: vc.add(new VectorCache());
+                 vc.add(new VectorCache());
+                 vc.add(new VectorCache());
+                 break;
+         default:vc.add(new VectorCache());
+         }
+
+       numPoints++;
+       cacheDirty = true;
+       }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float4D to the location'th place in our List of Points to interpolate through.  
+ *   
+ * @param location Index in our List to add the new Point at.
+ * @param v The Float4D to add.
+ */  
+  public synchronized void add(int location, Float4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: setUpVectors(0.0f,null);
+                break;
+        case 2: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(location,new VectorCache());
+        }
+
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all occurrences of Point v from the List of Points to interpolate through.  
+ * 
+ * @param v The Point to remove.
+ * @return <code>true</code> if we have removed at least one Point.
+ */
+  public synchronized boolean remove(Float4D v)
+    {
+    int n = vv.indexOf(v);
+    boolean found = false;
+   
+    while( n>=0 ) 
+      {
+      vv.remove(n);
+     
+      if( vn!=null ) vn.remove(0);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                setUpVectors(0.0f,null);
+                break;
+        default:vc.remove(n);
+        }
+
+      numPoints--;
+      found = true;
+      n = vv.indexOf(v);
+      }
+   
+    if( found ) 
+      {
+      cacheDirty=true;
+      }
+   
+    return found;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes a location'th Point from the List of Points we interpolate through.
+ * 
+ * @param location index of the Point we want to remove. 
+ * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
+ */
+  public synchronized boolean remove(int location)
+    {
+    if( location>=0 && location<numPoints ) 
+      {
+      vv.removeElementAt(location);
+       
+      if( vn!=null ) vn.remove(0);
+      
+      switch(numPoints)
+        {
+        case 0:
+        case 1: 
+        case 2: break;
+        case 3: vc.removeAllElements();
+                setUpVectors(0.0f,null);
+                break;
+        default:vc.removeElementAt(location);
+        }
+
+      numPoints--;
+      cacheDirty = true; 
+      return true;
+      }
+
+    return false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all Points.
+ */
+  public synchronized void removeAll()
+    {
+    numPoints = 0;
+    vv.removeAllElements();
+    vc.removeAllElements();
+    cacheDirty = false;
+   
+    if( vn!=null ) vn.removeAllElements();
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * <p>
+ * Since this is a 4-dimensional Interpolator, the resulting interpolated Float4D gets written
+ * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
+ * 
+ * @param buffer Float buffer we will write the resulting Float4D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
+ *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
+ */    
+  public synchronized void interpolate(float[] buffer, int offset, float time)
+    {  
+    switch(numPoints)
+      {
+      case 0: buffer[offset  ] = 0.0f;
+              buffer[offset+1] = 0.0f;
+              buffer[offset+2] = 0.0f;
+              buffer[offset+3] = 0.0f;
+              break;
+      case 1: curr = vv.elementAt(0);
+              buffer[offset  ] = curr.x;
+              buffer[offset+1] = curr.y;
+              buffer[offset+2] = curr.z;
+              buffer[offset+3] = curr.w;
+              break;
+      case 2: curr = vv.elementAt(0);
+              next = vv.elementAt(1);
+            
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+             
+              if( vn!=null )
+                {
+                time = noise(time,0);
+            
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (vec1X*mFactor1 + vec2X*mFactor2 + vec3X*mFactor3);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (vec1Y*mFactor1 + vec2Y*mFactor2 + vec3Y*mFactor3);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (vec1Z*mFactor1 + vec2Z*mFactor2 + vec3Z*mFactor3);
+                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (vec1W*mFactor1 + vec2W*mFactor2 + vec3W*mFactor3); 
+                }
+              else
+                {
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
+                buffer[offset+3] = (next.w-curr.w)*time + curr.w;
+                }
+                
+              break;
+      default:float t = time;
+        
+              switch(mMode)
+                {
+                case MODE_LOOP: time = time*numPoints;
+                                break;
+                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
+                                break;
+                case MODE_JUMP: time = time*(numPoints-1);
+                                break;
+                }
+     
+              int vecCurr = (int)time;
+              time = time-vecCurr;
+      
+              if( vecCurr>=0 && vecCurr<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
+                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext;   
+                  mVecCurr = vecCurr;
+                       
+                  switch(mMode)
+                    {
+                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
+                                    break;
+                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
+                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
+                                    break;
+                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
+                                    break;
+                    default       : vecNext = 0;                
+                    }
+     
+                  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();
+                  }
+            
+                tmp1 = vc.elementAt(vecCurr);
+               
+                if( vn!=null )
+                  {
+                  time = noise(time,vecCurr);
+              
+                  setUpVectors(time,tmp1);
+                 
+                  buffer[offset  ]= ((tmp1.ax*time+tmp1.bx)*time+tmp1.cx)*time+tmp1.dx + (vec1X*mFactor1 + vec2X*mFactor2 + vec3X*mFactor3);
+                  buffer[offset+1]= ((tmp1.ay*time+tmp1.by)*time+tmp1.cy)*time+tmp1.dy + (vec1Y*mFactor1 + vec2Y*mFactor2 + vec3Y*mFactor3);
+                  buffer[offset+2]= ((tmp1.az*time+tmp1.bz)*time+tmp1.cz)*time+tmp1.dz + (vec1Z*mFactor1 + vec2Z*mFactor2 + vec3Z*mFactor3);
+                  buffer[offset+3]= ((tmp1.aw*time+tmp1.bw)*time+tmp1.cw)*time+tmp1.dw + (vec1W*mFactor1 + vec2W*mFactor2 + vec3W*mFactor3);
+                  }
+                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;
+                  }
+ 
+                break;
+                }
+      }
+    }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/InterpolatorQuat.java b/src/main/java/org/distorted/library/type/InterpolatorQuat.java
new file mode 100644
index 0000000..9334e09
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/InterpolatorQuat.java
@@ -0,0 +1,392 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 4-dimensional implementation of the Interpolator class to interpolate between a list 
+* of Float4Ds.
+* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
+* SLERP. Noise not supported (yet?).
+*/
+
+public class InterpolatorQuat extends Interpolator 
+  {
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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
+    {
+    float omega, sinOmega,cosOmega;
+    float vx,vy,vz,vw;
+    }
+  
+  private Vector<VectorCache> vc;
+  private VectorCache tmp1, tmp2;
+
+  private Vector<Float4D> vv;
+  private Float4D curr, next;
+ 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//Abramowitz / Stegun
+
+  private static float arcCos(float x)
+    {
+    if( x<0 )
+      return 3.14159265358979f - (float)Math.sqrt(1+x)*(1.5707288f + 0.2121144f*x + 0.074261f*x*x + 0.0187293f*x*x*x);
+     
+    return (float)Math.sqrt(1-x)*(1.5707288f - 0.2121144f*x + 0.074261f*x*x - 0.0187293f*x*x*x);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Quaternion Interpolator doesn't support noise
+  
+  synchronized void createNoise()
+    {
+
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints>=2 )
+      {
+      int i, n;  
+     
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmp1= vc.elementAt(i);
+        tmp2= vc.elementAt(n);
+        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.cosOmega = curr.x*next.x + curr.y*next.y + curr.z*next.z + curr.w*next.w;
+      	
+        if( tmp1.cosOmega<0 && n!=0 )  // do not invert the last quaternion even if we'd have to go the long way around!
+          {
+          tmp1.cosOmega = -tmp1.cosOmega;
+          next.x = -next.x;
+          next.y = -next.y;
+          next.z = -next.z;
+          next.w = -next.w;
+          }
+      	
+        tmp1.sinOmega = (float)Math.sqrt(1-tmp1.cosOmega*tmp1.cosOmega);
+        tmp1.omega = arcCos(tmp1.cosOmega);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public InterpolatorQuat()
+    {
+    vv = new Vector<Float4D>();
+    vc = new Vector<VectorCache>();
+    numPoints = 0;
+    cacheDirty = false;
+    mMode = MODE_LOOP;
+    mDuration = 0;
+    mCount = 0.5f;
+    mNoise = 0.0f;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Float4D. 
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Float4D, if 0<=location&lt;getNumPoints(), or null otherwise. 
+ */  
+  public synchronized Float4D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * 
+ * @param location the index of the Point we are setting.
+ * @param x New value of its first float.
+ */
+  public synchronized void setPoint(int location, float x, float y, float z, float w)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x,y,z,w);
+        cacheDirty=true;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float4D to the end of our list of Points to interpolate through.
+ * <p>   
+ * Only a reference to the Point gets added to the List; this means that one can add a Point 
+ * here, and later on {@link Float4D#set(float,float,float,float)} it to some new value and 
+ * the change will be seamlessly reflected in the interpolated path.  
+ * <p>
+ * A Point can be added multiple times.
+ *   
+ * @param v The Point to add.
+ */    
+  public synchronized void add(Float4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+      
+      switch(numPoints)
+         {
+         case 0: 
+         case 1: vc.add(new VectorCache());
+                 vc.add(new VectorCache());
+        	     break;
+         default:vc.add(new VectorCache());
+         }
+
+       numPoints++;
+       cacheDirty = true;
+       }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Float4D to the location'th place in our List of Points to interpolate through.  
+ *   
+ * @param location Index in our List to add the new Point at.
+ * @param v The Float4D to add.
+ */  
+  public synchronized void add(int location, Float4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      switch(numPoints)
+        {
+        case 0: 
+        case 1: vc.add(new VectorCache());
+                vc.add(new VectorCache());
+                break;
+        default:vc.add(location,new VectorCache());
+        }
+
+      numPoints++;
+      cacheDirty = true;
+      }
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all occurrences of Point v from the List of Points to interpolate through.  
+ * 
+ * @param v The Point to remove.
+ * @return <code>true</code> if we have removed at least one Point.
+ */
+  public synchronized boolean remove(Float4D v)
+    {
+    int n = vv.indexOf(v);
+    boolean found = false;
+   
+    while( n>=0 ) 
+      {
+      vv.remove(n);
+     
+      switch(numPoints)
+        {
+        case 0:
+        case 1: break;
+        case 2: vc.removeAllElements();
+                break;
+        default:vc.remove(n);
+        }
+
+      numPoints--;
+      found = true;
+      n = vv.indexOf(v);
+      }
+   
+    if( found ) 
+      {
+      cacheDirty=true;
+      }
+   
+    return found;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes a location'th Point from the List of Points we interpolate through.
+ * 
+ * @param location index of the Point we want to remove. 
+ * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
+ */
+  public synchronized boolean remove(int location)
+    {
+    if( location>=0 && location<numPoints ) 
+      {
+      vv.removeElementAt(location);
+      
+      switch(numPoints)
+        {
+        case 0: 
+        case 1: break;
+        case 2: vc.removeAllElements();
+                break;
+        default:vc.removeElementAt(location);
+        }
+
+      numPoints--;
+      cacheDirty = true; 
+      return true;
+      }
+
+    return false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Removes all Points.
+ */
+  public synchronized void removeAll()
+    {
+    numPoints = 0;
+    vv.removeAllElements();
+    vc.removeAllElements();
+    cacheDirty = false;
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * Interpolation is done using the spherical linear algorithm, aka SLERP.
+ * <p>
+ * Since this is a 4-dimensional Interpolator, the resulting interpolated Float4D gets written
+ * to four locations in the buffer: buffer[offset], buffer[offset+1], buffer[offset+2] and buffer[offset+3]. 
+ * 
+ * @param buffer Float buffer we will write the resulting Float4D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
+ *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
+ */    
+  public synchronized void interpolate(float[] buffer, int offset, float time)
+    {  
+    switch(numPoints)
+      {
+      case 0: buffer[offset  ] = 0.0f;
+              buffer[offset+1] = 0.0f;
+              buffer[offset+2] = 0.0f;
+              buffer[offset+3] = 0.0f;
+              break;
+      case 1: curr = vv.elementAt(0);
+              buffer[offset  ] = curr.x; 
+              buffer[offset+1] = curr.y;
+              buffer[offset+2] = curr.z;
+              buffer[offset+3] = curr.w;
+              break;
+      default:float t = time;
+              float scale0, scale1;
+  
+              if( mMode==MODE_JUMP ) time = time*(numPoints-1);
+              else if( mMode==MODE_PATH || numPoints==2 ) time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
+              else time = time*numPoints;
+              
+              int vecNext, vecCurr = (int)time;
+              time = time-vecCurr;
+      
+              if( vecCurr>=0 && vecCurr<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
+                   
+                switch(mMode)
+                  {
+                  case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
+                                  break;
+                  case MODE_PATH: if( t<0.5f ) vecNext = vecCurr+1;  
+                                  else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
+                                  break;
+                  case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
+                                  break;
+                  default       : vecNext = 0;                
+                  }
+     
+                curr = vv.elementAt(vecCurr);
+                next = vv.elementAt(vecNext);
+                tmp1 = vc.elementAt(vecCurr);
+                tmp2 = vc.elementAt(vecNext);
+              
+                if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
+               
+                if( tmp1.sinOmega==0 )
+                  {
+                  scale0 = 0f;
+                  scale1 = 1f;
+                  }
+                else if( tmp1.cosOmega < 0.99 ) 
+                  {
+                  scale0 = (float)Math.sin( (1f-time)*tmp1.omega ) / tmp1.sinOmega;
+                  scale1 = (float)Math.sin(     time *tmp1.omega ) / tmp1.sinOmega;
+                  }
+                else 
+                  {
+                  scale0 = 1f-time;
+                  scale1 = time;
+                  }
+
+                buffer[offset  ] = scale0*curr.x + scale1*next.x;
+                buffer[offset+1] = scale0*curr.y + scale1*next.y; 
+                buffer[offset+2] = scale0*curr.z + scale1*next.z; 
+                buffer[offset+3] = scale0*curr.w + scale1*next.w; 
+                
+                break;
+                }
+      }
+    }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
