commit 814cfbc666aba78201baba51a013e1de682e2751
Author: leszek <leszek@koltunski.pl>
Date:   Tue Apr 22 13:12:09 2025 +0200

    Rename .java to .kt

diff --git a/src/main/java/org/distorted/library/type/Data1D.java b/src/main/java/org/distorted/library/type/Data1D.java
deleted file mode 100644
index b590475..0000000
--- a/src/main/java/org/distorted/library/type/Data1D.java
+++ /dev/null
@@ -1,32 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Marker Interface implemented by Static1D and Dynamic1D.
- * <p>
- * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
- */
-public interface Data1D
-  {
-  boolean get(float[] buffer, int offset, long time, long step);
-  }
diff --git a/src/main/java/org/distorted/library/type/Data1D.kt b/src/main/java/org/distorted/library/type/Data1D.kt
new file mode 100644
index 0000000..b590475
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Data1D.kt
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Marker Interface implemented by Static1D and Dynamic1D.
+ * <p>
+ * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
+ */
+public interface Data1D
+  {
+  boolean get(float[] buffer, int offset, long time, long step);
+  }
diff --git a/src/main/java/org/distorted/library/type/Data2D.java b/src/main/java/org/distorted/library/type/Data2D.java
deleted file mode 100644
index eab6d38..0000000
--- a/src/main/java/org/distorted/library/type/Data2D.java
+++ /dev/null
@@ -1,32 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Marker Interface implemented by Static2D and Dynamic2D.
- * <p>
- * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
- */
-public interface Data2D
-  {
-  boolean get(float[] buffer, int offset, long time, long step);
-  }
diff --git a/src/main/java/org/distorted/library/type/Data2D.kt b/src/main/java/org/distorted/library/type/Data2D.kt
new file mode 100644
index 0000000..eab6d38
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Data2D.kt
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Marker Interface implemented by Static2D and Dynamic2D.
+ * <p>
+ * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
+ */
+public interface Data2D
+  {
+  boolean get(float[] buffer, int offset, long time, long step);
+  }
diff --git a/src/main/java/org/distorted/library/type/Data3D.java b/src/main/java/org/distorted/library/type/Data3D.java
deleted file mode 100644
index aaf1178..0000000
--- a/src/main/java/org/distorted/library/type/Data3D.java
+++ /dev/null
@@ -1,32 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Marker Interface implemented by Static3D and Dynamic3D.
- * <p>
- * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
- */
-public interface Data3D
-  {
-  boolean get(float[] buffer, int offset, long time, long step);
-  }
diff --git a/src/main/java/org/distorted/library/type/Data3D.kt b/src/main/java/org/distorted/library/type/Data3D.kt
new file mode 100644
index 0000000..aaf1178
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Data3D.kt
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Marker Interface implemented by Static3D and Dynamic3D.
+ * <p>
+ * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
+ */
+public interface Data3D
+  {
+  boolean get(float[] buffer, int offset, long time, long step);
+  }
diff --git a/src/main/java/org/distorted/library/type/Data4D.java b/src/main/java/org/distorted/library/type/Data4D.java
deleted file mode 100644
index 7539508..0000000
--- a/src/main/java/org/distorted/library/type/Data4D.java
+++ /dev/null
@@ -1,32 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Marker Interface implemented by Static4D, Dynamic4D and DynamicQuat.
- * <p>
- * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
- */
-public interface Data4D
-  {
-  boolean get(float[] buffer, int offset, long time, long step);
-  }
diff --git a/src/main/java/org/distorted/library/type/Data4D.kt b/src/main/java/org/distorted/library/type/Data4D.kt
new file mode 100644
index 0000000..7539508
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Data4D.kt
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Marker Interface implemented by Static4D, Dynamic4D and DynamicQuat.
+ * <p>
+ * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
+ */
+public interface Data4D
+  {
+  boolean get(float[] buffer, int offset, long time, long step);
+  }
diff --git a/src/main/java/org/distorted/library/type/Data5D.java b/src/main/java/org/distorted/library/type/Data5D.java
deleted file mode 100644
index 2905e17..0000000
--- a/src/main/java/org/distorted/library/type/Data5D.java
+++ /dev/null
@@ -1,32 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Marker Interface implemented by Static5D and Dynamic5D.
- * <p>
- * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
- */
-public interface Data5D
-  {
-  boolean get(float[] buffer, int offset, long time, long step);
-  }
diff --git a/src/main/java/org/distorted/library/type/Data5D.kt b/src/main/java/org/distorted/library/type/Data5D.kt
new file mode 100644
index 0000000..2905e17
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Data5D.kt
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Marker Interface implemented by Static5D and Dynamic5D.
+ * <p>
+ * The point: so we can pass either Static and Dynamic to EffectQueues in a single parameter.
+ */
+public interface Data5D
+  {
+  boolean get(float[] buffer, int offset, long time, long step);
+  }
diff --git a/src/main/java/org/distorted/library/type/Dynamic.java b/src/main/java/org/distorted/library/type/Dynamic.java
deleted file mode 100644
index 124cdb7..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic.java
+++ /dev/null
@@ -1,888 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import org.distorted.library.main.DistortedLibrary;
-
-import java.util.Random;
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** A class to interpolate between a list of Statics.
-* <p><ul>
-* <li>if there is only one Point, just return it.
-* <li>if there are two Points, linearly bounce between them
-* <li>if there are more, interpolate a path between them. Exact way we interpolate depends on the MODE.
-* </ul>
-*/
-
-// The way Interpolation between more than 2 Points is done:
-// 
-// Def: let V[i] = (V[i](x), V[i](y), V[i](z)) be the direction and speed (i.e. velocity) we have to
-// be flying at Point P[i]
-//
-// Time it takes to fly though one segment P[i] --> P[i+1] : 0.0 --> 1.0
-//
-// We arbitrarily decide that V[i] should be equal to (|curr|*prev + |prev|*curr) / min(|prev|,|curr|)
-// where prev = P[i]-P[i-1] and curr = P[i+1]-P[i]
-//
-// 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) = V[i  ](x), Y'(0) = V[i  ](y), Z'(0) = V[i  ](z)
-// X(1) = P[i+1](x), Y(1)=P[i+1](y), Z(1)=P[i+1](z), X'(1) = V[i+1](x), Y'(1) = V[i+1](y), Z'(1) = V[i+1](z)
-//
-// we have the solution:  X(t) = at^3 + bt^2 + ct + d where
-// a =  2*P[i](x) +   V[i](x) - 2*P[i+1](x) + V[i+1](x)
-// b = -3*P[i](x) - 2*V[i](x) + 3*P[i+1](x) - V[i+1](x)
-// c =                V[i](x)
-// d =    P[i](x)
-//
-// and similarly Y(t) and Z(t).
-
-public abstract class Dynamic
-  {
-  /**
-   * Keep the speed of interpolation always changing. Time to cover one segment (distance between
-   * two consecutive points) always the same. Smoothly interpolate the speed between two segments.
-   */
-  public static final int SPEED_MODE_SMOOTH            = 0;
-  /**
-   * Make each segment have constant speed. Time to cover each segment is still the same, thus the
-   * speed will jump when passing through a point and then keep constant.
-   */
-  public static final int SPEED_MODE_SEGMENT_CONSTANT  = 1;
-  /**
-   * Have the speed be always, globally the same across all segments. Time to cover one segment will
-   * thus generally no longer be the same.
-   */
-  public static final int SPEED_MODE_GLOBALLY_CONSTANT = 2;  // TODO: not supported yet
-
-  /**
-   * One revolution takes us from the first point to the last and back to first through the shortest path.
-   */
-  public static final int MODE_LOOP = 0; 
-  /**
-   * One revolution takes us from the first point to the last and back to first through the same path.
-   */
-  public static final int MODE_PATH = 1; 
-  /**
-   * One revolution takes us from the first point to the last and jumps straight back to the first point.
-   */
-  public static final int MODE_JUMP = 2; 
-
-  /**
-   * The default mode of access. When in this mode, we are able to call interpolate() with points in time
-   * in any random order. This means one single Dynamic can be used in many effects simultaneously.
-   * On the other hand, when in this mode, it is not possible to smoothly interpolate when mDuration suddenly
-   * changes.
-   */
-  public static final int ACCESS_TYPE_RANDOM     = 0;
-  /**
-   * Set the mode to ACCESS_SEQUENTIAL if you need to change mDuration and you would rather have the Dynamic
-   * keep on smoothly interpolating.
-   * On the other hand, in this mode, a Dynamic can only be accessed in sequential manner, which means one
-   * Dynamic can only be used in one effect at a time.
-   */
-  public static final int ACCESS_TYPE_SEQUENTIAL = 1;
-
-  protected int mDimension;
-  protected int numPoints;
-  protected int mSegment;       // between which pair of points are we currently? (in case of PATH this is a bit complicated!)
-  protected boolean cacheDirty; // VectorCache not up to date
-  protected int mMode;          // LOOP, PATH or JUMP
-  protected long mDuration;     // number of milliseconds it takes to do a full loop/path from first vector to the last and back to the first
-  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 double mLastPos;
-  protected int mAccessType;
-  protected int mSpeedMode;
-  protected float mTmpTime;
-  protected int mTmpVec, mTmpSeg;
-
-  protected class VectorNoise
-    {
-    float[][] n;
-
-    VectorNoise()
-      {
-      n = new float[mDimension][NUM_NOISE];
-      }
-
-    void computeNoise()
-      {
-      n[0][0] = mRnd.nextFloat();
-      for(int i=1; i<NUM_NOISE; i++) n[0][i] = n[0][i-1]+mRnd.nextFloat();
-
-      float sum = n[0][NUM_NOISE-1] + mRnd.nextFloat();
-
-      for(int i=0; i<NUM_NOISE; i++)
-        {
-        n[0][i] /=sum;
-        for(int j=1; j<mDimension; j++) n[j][i] = mRnd.nextFloat()-0.5f;
-        }
-      }
-    }
-
-  protected Vector<VectorNoise> vn;
-  protected float[] mFactor;
-  protected float[] mNoise;
-  protected float[][] baseV;
-
-  ///////////////////////////////////////////////////////////////////////////////////////////////////
-  // the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = a[0]*T^3 + b[0]*T^2 + c[0]*t + d[0]  etc.
-  // (velocity) is the velocity vector.
-  // (cached) is the original vector from vv (copied here so when interpolating we can see if it is
-  // still valid and if not - rebuild the Cache
-
-  protected class VectorCache
-    {
-    float[] a;
-    float[] b;
-    float[] c;
-    float[] d;
-    float[] velocity;
-    float[] cached;
-    float[] path_ratio;
-
-    VectorCache()
-      {
-      a = new float[mDimension];
-      b = new float[mDimension];
-      c = new float[mDimension];
-      d = new float[mDimension];
-
-      velocity   = new float[mDimension];
-      cached     = new float[mDimension];
-      path_ratio = new float[NUM_RATIO];
-      }
-    }
-
-  protected Vector<VectorCache> vc;
-  protected VectorCache tmpCache1, tmpCache2;
-  protected float mConvexity;
-
-  private static final int NUM_RATIO = 10; // we attempt to 'smooth out' the speed in each segment -
-                                           // remember this many 'points' inside the Cache for each segment.
-
-  protected static final float[] mTmpRatio = new float[NUM_RATIO];
-
-  private float[] buf;
-  private float[] old;
-  private static final Random mRnd = new Random();
-  private 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.
-  private long mStartTime;
-  private long mCorrectedTime;
-  private static long mPausedTime;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// hide this from Javadoc
-  
-  protected Dynamic()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected Dynamic(int duration, float count, int dimension)
-    {
-    vc         = new Vector<>();
-    vn         = null;
-    numPoints  = 0;
-    cacheDirty = false;
-    mMode      = MODE_LOOP;
-    mDuration  = duration;
-    mCount     = count;
-    mDimension = dimension;
-    mSegment   = -1;
-    mLastPos   = -1;
-    mAccessType= ACCESS_TYPE_RANDOM;
-    mSpeedMode = SPEED_MODE_SMOOTH;
-    mConvexity = 1.0f;
-    mStartTime = -1;
-    mCorrectedTime = 0;
-
-    baseV      = new float[mDimension][mDimension];
-    buf        = new float[mDimension];
-    old        = new float[mDimension];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void initDynamic()
-    {
-    mStartTime = -1;
-    mCorrectedTime = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void onPause()
-    {
-    mPausedTime = System.currentTimeMillis();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected void computeSegmentAndTime(float time)
-    {
-    switch(mMode)
-      {
-      case MODE_LOOP: mTmpTime= time*numPoints;
-                      mTmpSeg = (int)mTmpTime;
-                      mTmpVec = mTmpSeg;
-                      break;
-      case MODE_PATH: mTmpSeg = (int)(2*time*(numPoints-1));
-
-                      if( time<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
-                        {               // and end position is slightly not equal to the end point => might not get autodeleted!
-                        mTmpTime = 2*time*(numPoints-1);
-                        mTmpVec = mTmpSeg;
-                        }
-                      else
-                        {
-                        mTmpTime = 2*(1-time)*(numPoints-1);
-                        mTmpVec  = 2*numPoints-3-mTmpSeg;
-                        }
-                      break;
-      case MODE_JUMP: mTmpTime= time*(numPoints-1);
-                      mTmpSeg = (int)mTmpTime;
-                      mTmpVec = mTmpSeg;
-                      break;
-      default       : mTmpVec = 0;
-                      mTmpSeg = 0;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float valueAtPoint(float t, VectorCache cache)
-    {
-    float tmp,sum = 0.0f;
-
-    for(int d=0; d<mDimension; d++)
-      {
-      tmp = (3*cache.a[d]*t + 2*cache.b[d])*t + cache.c[d];
-      sum += tmp*tmp;
-      }
-
-    return (float)Math.sqrt(sum);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected float smoothSpeed(float time, VectorCache cache)
-    {
-    float fndex = time*NUM_RATIO;
-    int index = (int)fndex;
-    float prev = index==0 ? 0.0f : cache.path_ratio[index-1];
-    float next = cache.path_ratio[index];
-
-    return prev + (next-prev)*(fndex-index);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// First, compute the approx length of the segment from time=0 to time=(i+1)/NUM_TIME and store this
-// in cache.path_ratio[i]. Then the last path_ratio is the length from 0 to 1, i.e. the total length
-// of the segment.
-// We do this by computing the integral from 0 to 1 of sqrt( (dx/dt)^2 + (dy/dt)^2 ) (i.e. the length
-// of the segment) using the approx 'trapezoids' integration method.
-//
-// Then, for every i, divide path_ratio[i] by the total length to get the percentage of total path
-// length covered at time i. At this time, path_ratio[3] = 0.45 means 'at time 3/NUM_RATIO, we cover
-// 0.45 = 45% of the total length of the segment.
-//
-// Finally, invert this function (for quicker lookups in smoothSpeed) so that after this step,
-// path_ratio[3] = 0.45 means 'at 45% of the time, we cover 3/NUM_RATIO distance'.
-
-  protected void smoothOutSegment(VectorCache cache)
-    {
-    float vPrev, sum = 0.0f;
-    float vNext = valueAtPoint(0.0f,cache);
-
-    for(int i=0; i<NUM_RATIO; i++)
-      {
-      vPrev = vNext;
-      vNext = valueAtPoint( (float)(i+1)/NUM_RATIO,cache);
-      sum += (vPrev+vNext);
-      cache.path_ratio[i] = sum;
-      }
-
-    float total = cache.path_ratio[NUM_RATIO-1];
-
-    for(int i=0; i<NUM_RATIO; i++) cache.path_ratio[i] /= total;
-
-    int writeIndex = 0;
-    float prev=0.0f, next, ratio= 1.0f/NUM_RATIO;
-
-    for(int readIndex=0; readIndex<NUM_RATIO; readIndex++)
-      {
-      next = cache.path_ratio[readIndex];
-
-      while( prev<ratio && ratio<=next )
-        {
-        float a = (next-ratio)/(next-prev);
-        mTmpRatio[writeIndex] = (readIndex+1-a)/NUM_RATIO;
-        writeIndex++;
-        ratio = (writeIndex+1.0f)/NUM_RATIO;
-        }
-
-      prev = next;
-      }
-
-    System.arraycopy(mTmpRatio, 0, cache.path_ratio, 0, NUM_RATIO);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  protected 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;
-    VectorNoise tmpN = vn.elementAt(vecNum);
-
-    float t = d-index;
-    t = t*t*(3-2*t);
-
-    switch(index)
-      {
-      case 0        : for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][0]*t;
-                      return time + mNoise[0]*(d*tmpN.n[0][0]-time);
-      case NUM_NOISE: for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][NUM_NOISE-1]*(1-t);
-                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
-                      lower = len + mNoise[0]*(tmpN.n[0][NUM_NOISE-1]-len);
-                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
-      default       : float ya,yb;
-
-                      for(int i=0;i<mDimension-1;i++)
-                        {
-                        yb = tmpN.n[i+1][index  ];
-                        ya = tmpN.n[i+1][index-1];
-                        mFactor[i] = mNoise[i+1]*((yb-ya)*t+ya);
-                        }
-
-                      len = ((float)index)/(NUM_NOISE+1);
-                      lower = len + mNoise[0]*(tmpN.n[0][index-1]-len);
-                      len = ((float)index+1)/(NUM_NOISE+1);
-                      upper = len + mNoise[0]*(tmpN.n[0][index  ]-len);
-
-                      return (upper-lower)*(d-index) + lower;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// debugging only
-
-  private void printBase(String str)
-    {
-    String s;
-    float t;
-
-    for(int i=0; i<mDimension; i++)
-      {
-      s = "";
-
-      for(int j=0; j<mDimension; j++)
-        {
-        t = ((int)(1000*baseV[i][j]))/(1000.0f);
-        s+=(" "+t);
-        }
-      DistortedLibrary.logMessage("Dynamic: "+str+" base "+i+" : " + s);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// debugging only
-
-  @SuppressWarnings("unused")
-  private void checkBase()
-    {
-    float tmp, cosA;
-    float[] len= new float[mDimension];
-    boolean error=false;
-
-    for(int i=0; i<mDimension; i++)
-      {
-      len[i] = 0.0f;
-
-      for(int k=0; k<mDimension; k++)
-        {
-        len[i] += baseV[i][k]*baseV[i][k];
-        }
-
-      if( len[i] == 0.0f || len[0]/len[i] < 0.95f || len[0]/len[i]>1.05f )
-        {
-        DistortedLibrary.logMessage("Dynamic: length of vector "+i+" : "+Math.sqrt(len[i]));
-        error = true;
-        }
-      }
-
-    for(int i=0; i<mDimension; i++)
-      for(int j=i+1; j<mDimension; j++)
-        {
-        tmp = 0.0f;
-
-        for(int k=0; k<mDimension; k++)
-          {
-          tmp += baseV[i][k]*baseV[j][k];
-          }
-
-        cosA = ( (len[i]==0.0f || len[j]==0.0f) ? 0.0f : tmp/(float)Math.sqrt(len[i]*len[j]));
-
-        if( cosA > 0.05f || cosA < -0.05f )
-          {
-          DistortedLibrary.logMessage("Dynamic: cos angle between vectors "+i+" and "+j+" : "+cosA);
-          error = true;
-          }
-        }
-
-    if( error ) printBase("");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getNext(int curr, float time)
-    {
-    switch(mMode)
-      {
-      case MODE_LOOP: return curr==numPoints-1 ? 0:curr+1;
-      case MODE_PATH: return time<0.5f ? (curr+1) : (curr==0 ? 1 : curr-1);
-      case MODE_JUMP: return curr==numPoints-1 ? 1:curr+1;
-      default       : return 0;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void checkAngle(int index)
-    {
-    float cosA = 0.0f;
-
-    for(int k=0;k<mDimension; k++)
-      cosA += baseV[index][k]*old[k];
-
-    if( cosA<0.0f )
-      {
-      for(int j=0; j<mDimension; j++)
-        baseV[index][j] = -baseV[index][j];
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// helper function in case we are interpolating through exactly 2 points
-
-  protected void computeOrthonormalBase2(Static curr, Static next)
-    {
-    switch(mDimension)
-      {
-      case 1: Static1D curr1 = (Static1D)curr;
-              Static1D next1 = (Static1D)next;
-              baseV[0][0] = (next1.x-curr1.x);
-              break;
-      case 2: Static2D curr2 = (Static2D)curr;
-              Static2D next2 = (Static2D)next;
-              baseV[0][0] = (next2.x-curr2.x);
-              baseV[0][1] = (next2.y-curr2.y);
-              break;
-      case 3: Static3D curr3 = (Static3D)curr;
-              Static3D next3 = (Static3D)next;
-              baseV[0][0] = (next3.x-curr3.x);
-              baseV[0][1] = (next3.y-curr3.y);
-              baseV[0][2] = (next3.z-curr3.z);
-              break;
-      case 4: Static4D curr4 = (Static4D)curr;
-              Static4D next4 = (Static4D)next;
-              baseV[0][0] = (next4.x-curr4.x);
-              baseV[0][1] = (next4.y-curr4.y);
-              baseV[0][2] = (next4.z-curr4.z);
-              baseV[0][3] = (next4.w-curr4.w);
-              break;
-      case 5: Static5D curr5 = (Static5D)curr;
-              Static5D next5 = (Static5D)next;
-              baseV[0][0] = (next5.x-curr5.x);
-              baseV[0][1] = (next5.y-curr5.y);
-              baseV[0][2] = (next5.z-curr5.z);
-              baseV[0][3] = (next5.w-curr5.w);
-              baseV[0][4] = (next5.v-curr5.v);
-              break;
-      default: throw new RuntimeException("Unsupported dimension");
-      }
-
-    if( baseV[0][0] == 0.0f )
-      {
-      baseV[1][0] = 1.0f;
-      baseV[1][1] = 0.0f;
-      }
-    else
-      {
-      baseV[1][0] = 0.0f;
-      baseV[1][1] = 1.0f;
-      }
-
-    for(int i=2; i<mDimension; i++)
-      {
-      baseV[1][i] = 0.0f;
-      }
-
-    computeOrthonormalBase();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// helper function in case we are interpolating through more than 2 points
-
-  protected void computeOrthonormalBaseMore(float time,VectorCache vc)
-    {
-    for(int i=0; i<mDimension; i++)
-      {
-      baseV[0][i] = (3*vc.a[i]*time+2*vc.b[i])*time+vc.c[i];   // first derivative, i.e. velocity vector
-      old[i]      = baseV[1][i];
-      baseV[1][i] =  6*vc.a[i]*time+2*vc.b[i];                 // second derivative,i.e. acceleration vector
-      }
-
-    computeOrthonormalBase();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// When this function gets called, baseV[0] and baseV[1] should have been filled with two mDimension-al
-// vectors. This function then fills the rest of the baseV array with a mDimension-al Orthonormal base.
-// (mDimension-2 vectors, pairwise orthogonal to each other and to the original 2). The function always
-// leaves base[0] alone but generally speaking must adjust base[1] to make it orthogonal to base[0]!
-// The whole baseV is then used to compute Noise.
-//
-// When computing noise of a point travelling along a N-dimensional path, there are three cases:
-// a) we may be interpolating through 1 point, i.e. standing in place - nothing to do in this case
-// b) we may be interpolating through 2 points, i.e. travelling along a straight line between them -
-//    then pass the velocity vector in baseV[0] and anything linearly independent in base[1].
-//    The output will then be discontinuous in dimensions>2 (sad corollary from the Hairy Ball Theorem)
-//    but we don't care - we are travelling along a straight line, so velocity (aka baseV[0]!) does
-//    not change.
-// c) we may be interpolating through more than 2 points. Then interpolation formulas ensure the path
-//    will never be a straight line, even locally -> we can pass in baseV[0] and baseV[1] the velocity
-//    and the acceleration (first and second derivatives of the path) which are then guaranteed to be
-//    linearly independent. Then we can ensure this is continuous in dimensions <=4. This leaves
-//    dimension 5 (ATM WAVE is 5-dimensional) discontinuous -> WAVE will suffer from chaotic noise.
-//
-// Bear in mind here the 'normal' in 'orthonormal' means 'length equal to the length of the original
-// velocity vector' (rather than the standard 1)
-
-  protected void computeOrthonormalBase()
-    {
-    int last_non_zero=-1;
-    float tmp;
-
-    for(int i=0; i<mDimension; i++)
-      if( baseV[0][i] != 0.0f )
-        last_non_zero=i;
-
-    if( last_non_zero==-1 )                                               ///
-      {                                                                   //  velocity is the 0 vector -> two
-      for(int i=0; i<mDimension-1; i++)                                   //  consecutive points we are interpolating
-        for(int j=0; j<mDimension; j++)                                   //  through are identical -> no noise,
-          baseV[i+1][j]= 0.0f;                                            //  set the base to 0 vectors.
-      }                                                                   ///
-    else
-      {
-      for(int i=1; i<mDimension; i++)                                     /// One iteration computes baseV[i][*]
-        {                                                                 //  (aka b[i]), the i-th orthonormal vector.
-        buf[i-1]=0.0f;                                                    //
-                                                                          //  We can use (modified!) Gram-Schmidt.
-        for(int k=0; k<mDimension; k++)                                   //
-          {                                                               //
-          if( i>=2 )                                                      //  b[0] = b[0]
-            {                                                             //  b[1] = b[1] - (<b[1],b[0]>/<b[0],b[0]>)*b[0]
-            old[k] = baseV[i][k];                                         //  b[2] = b[2] - (<b[2],b[0]>/<b[0],b[0]>)*b[0] - (<b[2],b[1]>/<b[1],b[1]>)*b[1]
-            baseV[i][k]= (k==i-(last_non_zero>=i?1:0)) ? 1.0f : 0.0f;     //  b[3] = b[3] - (<b[3],b[0]>/<b[0],b[0]>)*b[0] - (<b[3],b[1]>/<b[1],b[1]>)*b[1] - (<b[3],b[2]>/<b[2],b[2]>)*b[2]
-            }                                                             //  (...)
-                                                                          //  then b[i] = b[i] / |b[i]|  ( Here really b[i] = b[i] / (|b[0]|/|b[i]|)
-          tmp = baseV[i-1][k];                                            //
-          buf[i-1] += tmp*tmp;                                            //
-          }                                                               //
-                                                                          //
-        for(int j=0; j<i; j++)                                            //
-          {                                                               //
-          tmp = 0.0f;                                                     //
-          for(int k=0;k<mDimension; k++) tmp += baseV[i][k]*baseV[j][k];  //
-          tmp /= buf[j];                                                  //
-          for(int k=0;k<mDimension; k++) baseV[i][k] -= tmp*baseV[j][k];  //
-          }                                                               //
-                                                                          //
-        checkAngle(i);                                                    //
-        }                                                                 /// end compute baseV[i][*]
-
-      buf[mDimension-1]=0.0f;                                             /// Normalize
-      for(int k=0; k<mDimension; k++)                                     //
-        {                                                                 //
-        tmp = baseV[mDimension-1][k];                                     //
-        buf[mDimension-1] += tmp*tmp;                                     //
-        }                                                                 //
-                                                                          //
-      for(int i=1; i<mDimension; i++)                                     //
-        {                                                                 //
-        tmp = (float)Math.sqrt(buf[0]/buf[i]);                            //
-        for(int k=0;k<mDimension; k++) baseV[i][k] *= tmp;                //
-        }                                                                 /// End Normalize
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  abstract void interpolate(float[] buffer, int offset, float time);
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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 straight back to the first.
- * </ul>
- * 
- * @param mode {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- */
-  public void setMode(int mode)
-    {
-    mMode = mode;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the number of Points this Dynamic has been fed with.
- *   
- * @return the number of Points we are currently interpolating through.
- */
-  public synchronized int getNumPoints()
-    {
-    return numPoints;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets how many revolutions we want to do.
- * <p>
- * Does not have to be an integer. What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- * Count<=0 means 'go on interpolating indefinitely'.
- * 
- * @param count the number of times we want to interpolate between our collection of Points.
- */
-  public void setCount(float count)
-    {
-    mCount = count;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the number of revolutions this Dynamic will make.
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @return the number revolutions this Dynamic will make.
- */
-  public float getCount()
-    {
-    return mCount;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Start running from the beginning again.
- *
- * If a Dynamic has been used already, and we want to use it again and start interpolating from the
- * first Point, first we need to reset it using this method.
- */
-  public void resetToBeginning()
-    {
-    mStartTime = -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @param duration Number of milliseconds one revolution will take.
- *                 What constitutes 'one revolution' depends on the MODE:
- *                 {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- */
-  public void setDuration(long duration)
-    {
-    mDuration = duration;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @return Number of milliseconds one revolution will take.
- */
-  public long getDuration()
-    {
-    return mDuration;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @param convexity If set to the default (1.0f) then interpolation between 4 points
- *                  (1,0) (0,1) (-1,0) (0,-1) will be the natural circle centered at (0,0) with radius 1.
- *                  The less it is, the less convex the circle becomes, ultimately when convexity=0.0f
- *                  then the interpolation shape will be straight lines connecting the four points.
- *                  Further setting this to negative values will make the shape concave.
- *                  Valid values: all floats. (although probably only something around (0,2) actually
- *                  makes sense)
- */
-  public void setConvexity(float convexity)
-    {
-    if( mConvexity!=convexity )
-      {
-      mConvexity = convexity;
-      cacheDirty = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @return See {@link Dynamic#setConvexity(float)}
- */
-  public float getConvexity()
-    {
-    return mConvexity;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the access type this Dynamic will be working in.
- *
- * @param type {@link Dynamic#ACCESS_TYPE_RANDOM} or {@link Dynamic#ACCESS_TYPE_SEQUENTIAL}.
- */
-  public void setAccessType(int type)
-    {
-    mAccessType = type;
-    mLastPos = -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @return See {@link Dynamic#setSpeedMode(int)}
- */
-  public float getSpeedMode()
-    {
-    return mSpeedMode;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the way we compute the interpolation speed.
- *
- * @param mode {@link Dynamic#SPEED_MODE_SMOOTH} or {@link Dynamic#SPEED_MODE_SEGMENT_CONSTANT} or
- *             {@link Dynamic#SPEED_MODE_GLOBALLY_CONSTANT}
- */
-  public void setSpeedMode(int mode)
-    {
-    if( mSpeedMode!=mode )
-      {
-      if( mSpeedMode==SPEED_MODE_SMOOTH )
-        {
-        for(int i=0; i<numPoints; i++)
-          {
-          tmpCache1 = vc.elementAt(i);
-          smoothOutSegment(tmpCache1);
-          }
-        }
-
-      mSpeedMode = mode;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the Dimension, ie number of floats in a single Point this Dynamic interpolates through.
- *
- * @return number of floats in a single Point (ie its dimension) contained in the Dynamic.
- */
-  public int getDimension()
-    {
-    return mDimension;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
- * <p>
- * This version differs from the previous in that it returns a boolean value which indicates whether
- * the interpolation is finished.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time   Time of interpolation. Time=0.0 is the beginning of the first revolution, time=1.0 - the end
- *               of the first revolution, time=2.5 - the middle of the third revolution.
- *               What constitutes 'one revolution' depends on the MODE:
- *               {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- * @param step   Time difference between now and the last time we called this function. Needed to figure
- *               out if the previous time we were called the effect wasn't finished yet, but now it is.
- * @return true if the interpolation reached its end.
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    if( mDuration<=0.0f )
-      {
-      interpolate(buffer,offset,mCount-(int)mCount);
-      return false;
-      }
-
-    if( mStartTime==-1 )
-      {
-      mStartTime = time;
-      mLastPos   = -1;
-      }
-
-    long diff = time-mPausedTime;
-
-    if( mStartTime<mPausedTime && mCorrectedTime<mPausedTime && diff>=0 && diff<=step )
-      {
-      mCorrectedTime = mPausedTime;
-      mStartTime += diff;
-      step -= diff;
-      }
-
-    time -= mStartTime;
-
-    if( time+step > mDuration*mCount && mCount>0.0f )
-      {
-      interpolate(buffer,offset,mCount-(int)mCount);
-      return true;
-      }
-
-    double pos;
-
-    if( mAccessType ==ACCESS_TYPE_SEQUENTIAL )
-      {
-      pos = mLastPos<0 ? (double)time/mDuration : (double)step/mDuration + mLastPos;
-      mLastPos = pos;
-      }
-    else
-      {
-      pos = (double)time/mDuration;
-      }
-
-    interpolate(buffer,offset, (float)(pos-(int)pos) );
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Dynamic.kt b/src/main/java/org/distorted/library/type/Dynamic.kt
new file mode 100644
index 0000000..124cdb7
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic.kt
@@ -0,0 +1,888 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import org.distorted.library.main.DistortedLibrary;
+
+import java.util.Random;
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** A class to interpolate between a list of Statics.
+* <p><ul>
+* <li>if there is only one Point, just return it.
+* <li>if there are two Points, linearly bounce between them
+* <li>if there are more, interpolate a path between them. Exact way we interpolate depends on the MODE.
+* </ul>
+*/
+
+// The way Interpolation between more than 2 Points is done:
+// 
+// Def: let V[i] = (V[i](x), V[i](y), V[i](z)) be the direction and speed (i.e. velocity) we have to
+// be flying at Point P[i]
+//
+// Time it takes to fly though one segment P[i] --> P[i+1] : 0.0 --> 1.0
+//
+// We arbitrarily decide that V[i] should be equal to (|curr|*prev + |prev|*curr) / min(|prev|,|curr|)
+// where prev = P[i]-P[i-1] and curr = P[i+1]-P[i]
+//
+// 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) = V[i  ](x), Y'(0) = V[i  ](y), Z'(0) = V[i  ](z)
+// X(1) = P[i+1](x), Y(1)=P[i+1](y), Z(1)=P[i+1](z), X'(1) = V[i+1](x), Y'(1) = V[i+1](y), Z'(1) = V[i+1](z)
+//
+// we have the solution:  X(t) = at^3 + bt^2 + ct + d where
+// a =  2*P[i](x) +   V[i](x) - 2*P[i+1](x) + V[i+1](x)
+// b = -3*P[i](x) - 2*V[i](x) + 3*P[i+1](x) - V[i+1](x)
+// c =                V[i](x)
+// d =    P[i](x)
+//
+// and similarly Y(t) and Z(t).
+
+public abstract class Dynamic
+  {
+  /**
+   * Keep the speed of interpolation always changing. Time to cover one segment (distance between
+   * two consecutive points) always the same. Smoothly interpolate the speed between two segments.
+   */
+  public static final int SPEED_MODE_SMOOTH            = 0;
+  /**
+   * Make each segment have constant speed. Time to cover each segment is still the same, thus the
+   * speed will jump when passing through a point and then keep constant.
+   */
+  public static final int SPEED_MODE_SEGMENT_CONSTANT  = 1;
+  /**
+   * Have the speed be always, globally the same across all segments. Time to cover one segment will
+   * thus generally no longer be the same.
+   */
+  public static final int SPEED_MODE_GLOBALLY_CONSTANT = 2;  // TODO: not supported yet
+
+  /**
+   * One revolution takes us from the first point to the last and back to first through the shortest path.
+   */
+  public static final int MODE_LOOP = 0; 
+  /**
+   * One revolution takes us from the first point to the last and back to first through the same path.
+   */
+  public static final int MODE_PATH = 1; 
+  /**
+   * One revolution takes us from the first point to the last and jumps straight back to the first point.
+   */
+  public static final int MODE_JUMP = 2; 
+
+  /**
+   * The default mode of access. When in this mode, we are able to call interpolate() with points in time
+   * in any random order. This means one single Dynamic can be used in many effects simultaneously.
+   * On the other hand, when in this mode, it is not possible to smoothly interpolate when mDuration suddenly
+   * changes.
+   */
+  public static final int ACCESS_TYPE_RANDOM     = 0;
+  /**
+   * Set the mode to ACCESS_SEQUENTIAL if you need to change mDuration and you would rather have the Dynamic
+   * keep on smoothly interpolating.
+   * On the other hand, in this mode, a Dynamic can only be accessed in sequential manner, which means one
+   * Dynamic can only be used in one effect at a time.
+   */
+  public static final int ACCESS_TYPE_SEQUENTIAL = 1;
+
+  protected int mDimension;
+  protected int numPoints;
+  protected int mSegment;       // between which pair of points are we currently? (in case of PATH this is a bit complicated!)
+  protected boolean cacheDirty; // VectorCache not up to date
+  protected int mMode;          // LOOP, PATH or JUMP
+  protected long mDuration;     // number of milliseconds it takes to do a full loop/path from first vector to the last and back to the first
+  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 double mLastPos;
+  protected int mAccessType;
+  protected int mSpeedMode;
+  protected float mTmpTime;
+  protected int mTmpVec, mTmpSeg;
+
+  protected class VectorNoise
+    {
+    float[][] n;
+
+    VectorNoise()
+      {
+      n = new float[mDimension][NUM_NOISE];
+      }
+
+    void computeNoise()
+      {
+      n[0][0] = mRnd.nextFloat();
+      for(int i=1; i<NUM_NOISE; i++) n[0][i] = n[0][i-1]+mRnd.nextFloat();
+
+      float sum = n[0][NUM_NOISE-1] + mRnd.nextFloat();
+
+      for(int i=0; i<NUM_NOISE; i++)
+        {
+        n[0][i] /=sum;
+        for(int j=1; j<mDimension; j++) n[j][i] = mRnd.nextFloat()-0.5f;
+        }
+      }
+    }
+
+  protected Vector<VectorNoise> vn;
+  protected float[] mFactor;
+  protected float[] mNoise;
+  protected float[][] baseV;
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  // the coefficients of the X(t), Y(t) and Z(t) polynomials: X(t) = a[0]*T^3 + b[0]*T^2 + c[0]*t + d[0]  etc.
+  // (velocity) is the velocity vector.
+  // (cached) is the original vector from vv (copied here so when interpolating we can see if it is
+  // still valid and if not - rebuild the Cache
+
+  protected class VectorCache
+    {
+    float[] a;
+    float[] b;
+    float[] c;
+    float[] d;
+    float[] velocity;
+    float[] cached;
+    float[] path_ratio;
+
+    VectorCache()
+      {
+      a = new float[mDimension];
+      b = new float[mDimension];
+      c = new float[mDimension];
+      d = new float[mDimension];
+
+      velocity   = new float[mDimension];
+      cached     = new float[mDimension];
+      path_ratio = new float[NUM_RATIO];
+      }
+    }
+
+  protected Vector<VectorCache> vc;
+  protected VectorCache tmpCache1, tmpCache2;
+  protected float mConvexity;
+
+  private static final int NUM_RATIO = 10; // we attempt to 'smooth out' the speed in each segment -
+                                           // remember this many 'points' inside the Cache for each segment.
+
+  protected static final float[] mTmpRatio = new float[NUM_RATIO];
+
+  private float[] buf;
+  private float[] old;
+  private static final Random mRnd = new Random();
+  private 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.
+  private long mStartTime;
+  private long mCorrectedTime;
+  private static long mPausedTime;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// hide this from Javadoc
+  
+  protected Dynamic()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected Dynamic(int duration, float count, int dimension)
+    {
+    vc         = new Vector<>();
+    vn         = null;
+    numPoints  = 0;
+    cacheDirty = false;
+    mMode      = MODE_LOOP;
+    mDuration  = duration;
+    mCount     = count;
+    mDimension = dimension;
+    mSegment   = -1;
+    mLastPos   = -1;
+    mAccessType= ACCESS_TYPE_RANDOM;
+    mSpeedMode = SPEED_MODE_SMOOTH;
+    mConvexity = 1.0f;
+    mStartTime = -1;
+    mCorrectedTime = 0;
+
+    baseV      = new float[mDimension][mDimension];
+    buf        = new float[mDimension];
+    old        = new float[mDimension];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void initDynamic()
+    {
+    mStartTime = -1;
+    mCorrectedTime = 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void onPause()
+    {
+    mPausedTime = System.currentTimeMillis();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected void computeSegmentAndTime(float time)
+    {
+    switch(mMode)
+      {
+      case MODE_LOOP: mTmpTime= time*numPoints;
+                      mTmpSeg = (int)mTmpTime;
+                      mTmpVec = mTmpSeg;
+                      break;
+      case MODE_PATH: mTmpSeg = (int)(2*time*(numPoints-1));
+
+                      if( time<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
+                        {               // and end position is slightly not equal to the end point => might not get autodeleted!
+                        mTmpTime = 2*time*(numPoints-1);
+                        mTmpVec = mTmpSeg;
+                        }
+                      else
+                        {
+                        mTmpTime = 2*(1-time)*(numPoints-1);
+                        mTmpVec  = 2*numPoints-3-mTmpSeg;
+                        }
+                      break;
+      case MODE_JUMP: mTmpTime= time*(numPoints-1);
+                      mTmpSeg = (int)mTmpTime;
+                      mTmpVec = mTmpSeg;
+                      break;
+      default       : mTmpVec = 0;
+                      mTmpSeg = 0;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float valueAtPoint(float t, VectorCache cache)
+    {
+    float tmp,sum = 0.0f;
+
+    for(int d=0; d<mDimension; d++)
+      {
+      tmp = (3*cache.a[d]*t + 2*cache.b[d])*t + cache.c[d];
+      sum += tmp*tmp;
+      }
+
+    return (float)Math.sqrt(sum);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected float smoothSpeed(float time, VectorCache cache)
+    {
+    float fndex = time*NUM_RATIO;
+    int index = (int)fndex;
+    float prev = index==0 ? 0.0f : cache.path_ratio[index-1];
+    float next = cache.path_ratio[index];
+
+    return prev + (next-prev)*(fndex-index);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// First, compute the approx length of the segment from time=0 to time=(i+1)/NUM_TIME and store this
+// in cache.path_ratio[i]. Then the last path_ratio is the length from 0 to 1, i.e. the total length
+// of the segment.
+// We do this by computing the integral from 0 to 1 of sqrt( (dx/dt)^2 + (dy/dt)^2 ) (i.e. the length
+// of the segment) using the approx 'trapezoids' integration method.
+//
+// Then, for every i, divide path_ratio[i] by the total length to get the percentage of total path
+// length covered at time i. At this time, path_ratio[3] = 0.45 means 'at time 3/NUM_RATIO, we cover
+// 0.45 = 45% of the total length of the segment.
+//
+// Finally, invert this function (for quicker lookups in smoothSpeed) so that after this step,
+// path_ratio[3] = 0.45 means 'at 45% of the time, we cover 3/NUM_RATIO distance'.
+
+  protected void smoothOutSegment(VectorCache cache)
+    {
+    float vPrev, sum = 0.0f;
+    float vNext = valueAtPoint(0.0f,cache);
+
+    for(int i=0; i<NUM_RATIO; i++)
+      {
+      vPrev = vNext;
+      vNext = valueAtPoint( (float)(i+1)/NUM_RATIO,cache);
+      sum += (vPrev+vNext);
+      cache.path_ratio[i] = sum;
+      }
+
+    float total = cache.path_ratio[NUM_RATIO-1];
+
+    for(int i=0; i<NUM_RATIO; i++) cache.path_ratio[i] /= total;
+
+    int writeIndex = 0;
+    float prev=0.0f, next, ratio= 1.0f/NUM_RATIO;
+
+    for(int readIndex=0; readIndex<NUM_RATIO; readIndex++)
+      {
+      next = cache.path_ratio[readIndex];
+
+      while( prev<ratio && ratio<=next )
+        {
+        float a = (next-ratio)/(next-prev);
+        mTmpRatio[writeIndex] = (readIndex+1-a)/NUM_RATIO;
+        writeIndex++;
+        ratio = (writeIndex+1.0f)/NUM_RATIO;
+        }
+
+      prev = next;
+      }
+
+    System.arraycopy(mTmpRatio, 0, cache.path_ratio, 0, NUM_RATIO);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  protected 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;
+    VectorNoise tmpN = vn.elementAt(vecNum);
+
+    float t = d-index;
+    t = t*t*(3-2*t);
+
+    switch(index)
+      {
+      case 0        : for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][0]*t;
+                      return time + mNoise[0]*(d*tmpN.n[0][0]-time);
+      case NUM_NOISE: for(int i=0;i<mDimension-1;i++) mFactor[i] = mNoise[i+1]*tmpN.n[i+1][NUM_NOISE-1]*(1-t);
+                      len = ((float)NUM_NOISE)/(NUM_NOISE+1);
+                      lower = len + mNoise[0]*(tmpN.n[0][NUM_NOISE-1]-len);
+                      return (1.0f-lower)*(d-NUM_NOISE) + lower;
+      default       : float ya,yb;
+
+                      for(int i=0;i<mDimension-1;i++)
+                        {
+                        yb = tmpN.n[i+1][index  ];
+                        ya = tmpN.n[i+1][index-1];
+                        mFactor[i] = mNoise[i+1]*((yb-ya)*t+ya);
+                        }
+
+                      len = ((float)index)/(NUM_NOISE+1);
+                      lower = len + mNoise[0]*(tmpN.n[0][index-1]-len);
+                      len = ((float)index+1)/(NUM_NOISE+1);
+                      upper = len + mNoise[0]*(tmpN.n[0][index  ]-len);
+
+                      return (upper-lower)*(d-index) + lower;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// debugging only
+
+  private void printBase(String str)
+    {
+    String s;
+    float t;
+
+    for(int i=0; i<mDimension; i++)
+      {
+      s = "";
+
+      for(int j=0; j<mDimension; j++)
+        {
+        t = ((int)(1000*baseV[i][j]))/(1000.0f);
+        s+=(" "+t);
+        }
+      DistortedLibrary.logMessage("Dynamic: "+str+" base "+i+" : " + s);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// debugging only
+
+  @SuppressWarnings("unused")
+  private void checkBase()
+    {
+    float tmp, cosA;
+    float[] len= new float[mDimension];
+    boolean error=false;
+
+    for(int i=0; i<mDimension; i++)
+      {
+      len[i] = 0.0f;
+
+      for(int k=0; k<mDimension; k++)
+        {
+        len[i] += baseV[i][k]*baseV[i][k];
+        }
+
+      if( len[i] == 0.0f || len[0]/len[i] < 0.95f || len[0]/len[i]>1.05f )
+        {
+        DistortedLibrary.logMessage("Dynamic: length of vector "+i+" : "+Math.sqrt(len[i]));
+        error = true;
+        }
+      }
+
+    for(int i=0; i<mDimension; i++)
+      for(int j=i+1; j<mDimension; j++)
+        {
+        tmp = 0.0f;
+
+        for(int k=0; k<mDimension; k++)
+          {
+          tmp += baseV[i][k]*baseV[j][k];
+          }
+
+        cosA = ( (len[i]==0.0f || len[j]==0.0f) ? 0.0f : tmp/(float)Math.sqrt(len[i]*len[j]));
+
+        if( cosA > 0.05f || cosA < -0.05f )
+          {
+          DistortedLibrary.logMessage("Dynamic: cos angle between vectors "+i+" and "+j+" : "+cosA);
+          error = true;
+          }
+        }
+
+    if( error ) printBase("");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNext(int curr, float time)
+    {
+    switch(mMode)
+      {
+      case MODE_LOOP: return curr==numPoints-1 ? 0:curr+1;
+      case MODE_PATH: return time<0.5f ? (curr+1) : (curr==0 ? 1 : curr-1);
+      case MODE_JUMP: return curr==numPoints-1 ? 1:curr+1;
+      default       : return 0;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void checkAngle(int index)
+    {
+    float cosA = 0.0f;
+
+    for(int k=0;k<mDimension; k++)
+      cosA += baseV[index][k]*old[k];
+
+    if( cosA<0.0f )
+      {
+      for(int j=0; j<mDimension; j++)
+        baseV[index][j] = -baseV[index][j];
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// helper function in case we are interpolating through exactly 2 points
+
+  protected void computeOrthonormalBase2(Static curr, Static next)
+    {
+    switch(mDimension)
+      {
+      case 1: Static1D curr1 = (Static1D)curr;
+              Static1D next1 = (Static1D)next;
+              baseV[0][0] = (next1.x-curr1.x);
+              break;
+      case 2: Static2D curr2 = (Static2D)curr;
+              Static2D next2 = (Static2D)next;
+              baseV[0][0] = (next2.x-curr2.x);
+              baseV[0][1] = (next2.y-curr2.y);
+              break;
+      case 3: Static3D curr3 = (Static3D)curr;
+              Static3D next3 = (Static3D)next;
+              baseV[0][0] = (next3.x-curr3.x);
+              baseV[0][1] = (next3.y-curr3.y);
+              baseV[0][2] = (next3.z-curr3.z);
+              break;
+      case 4: Static4D curr4 = (Static4D)curr;
+              Static4D next4 = (Static4D)next;
+              baseV[0][0] = (next4.x-curr4.x);
+              baseV[0][1] = (next4.y-curr4.y);
+              baseV[0][2] = (next4.z-curr4.z);
+              baseV[0][3] = (next4.w-curr4.w);
+              break;
+      case 5: Static5D curr5 = (Static5D)curr;
+              Static5D next5 = (Static5D)next;
+              baseV[0][0] = (next5.x-curr5.x);
+              baseV[0][1] = (next5.y-curr5.y);
+              baseV[0][2] = (next5.z-curr5.z);
+              baseV[0][3] = (next5.w-curr5.w);
+              baseV[0][4] = (next5.v-curr5.v);
+              break;
+      default: throw new RuntimeException("Unsupported dimension");
+      }
+
+    if( baseV[0][0] == 0.0f )
+      {
+      baseV[1][0] = 1.0f;
+      baseV[1][1] = 0.0f;
+      }
+    else
+      {
+      baseV[1][0] = 0.0f;
+      baseV[1][1] = 1.0f;
+      }
+
+    for(int i=2; i<mDimension; i++)
+      {
+      baseV[1][i] = 0.0f;
+      }
+
+    computeOrthonormalBase();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// helper function in case we are interpolating through more than 2 points
+
+  protected void computeOrthonormalBaseMore(float time,VectorCache vc)
+    {
+    for(int i=0; i<mDimension; i++)
+      {
+      baseV[0][i] = (3*vc.a[i]*time+2*vc.b[i])*time+vc.c[i];   // first derivative, i.e. velocity vector
+      old[i]      = baseV[1][i];
+      baseV[1][i] =  6*vc.a[i]*time+2*vc.b[i];                 // second derivative,i.e. acceleration vector
+      }
+
+    computeOrthonormalBase();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// When this function gets called, baseV[0] and baseV[1] should have been filled with two mDimension-al
+// vectors. This function then fills the rest of the baseV array with a mDimension-al Orthonormal base.
+// (mDimension-2 vectors, pairwise orthogonal to each other and to the original 2). The function always
+// leaves base[0] alone but generally speaking must adjust base[1] to make it orthogonal to base[0]!
+// The whole baseV is then used to compute Noise.
+//
+// When computing noise of a point travelling along a N-dimensional path, there are three cases:
+// a) we may be interpolating through 1 point, i.e. standing in place - nothing to do in this case
+// b) we may be interpolating through 2 points, i.e. travelling along a straight line between them -
+//    then pass the velocity vector in baseV[0] and anything linearly independent in base[1].
+//    The output will then be discontinuous in dimensions>2 (sad corollary from the Hairy Ball Theorem)
+//    but we don't care - we are travelling along a straight line, so velocity (aka baseV[0]!) does
+//    not change.
+// c) we may be interpolating through more than 2 points. Then interpolation formulas ensure the path
+//    will never be a straight line, even locally -> we can pass in baseV[0] and baseV[1] the velocity
+//    and the acceleration (first and second derivatives of the path) which are then guaranteed to be
+//    linearly independent. Then we can ensure this is continuous in dimensions <=4. This leaves
+//    dimension 5 (ATM WAVE is 5-dimensional) discontinuous -> WAVE will suffer from chaotic noise.
+//
+// Bear in mind here the 'normal' in 'orthonormal' means 'length equal to the length of the original
+// velocity vector' (rather than the standard 1)
+
+  protected void computeOrthonormalBase()
+    {
+    int last_non_zero=-1;
+    float tmp;
+
+    for(int i=0; i<mDimension; i++)
+      if( baseV[0][i] != 0.0f )
+        last_non_zero=i;
+
+    if( last_non_zero==-1 )                                               ///
+      {                                                                   //  velocity is the 0 vector -> two
+      for(int i=0; i<mDimension-1; i++)                                   //  consecutive points we are interpolating
+        for(int j=0; j<mDimension; j++)                                   //  through are identical -> no noise,
+          baseV[i+1][j]= 0.0f;                                            //  set the base to 0 vectors.
+      }                                                                   ///
+    else
+      {
+      for(int i=1; i<mDimension; i++)                                     /// One iteration computes baseV[i][*]
+        {                                                                 //  (aka b[i]), the i-th orthonormal vector.
+        buf[i-1]=0.0f;                                                    //
+                                                                          //  We can use (modified!) Gram-Schmidt.
+        for(int k=0; k<mDimension; k++)                                   //
+          {                                                               //
+          if( i>=2 )                                                      //  b[0] = b[0]
+            {                                                             //  b[1] = b[1] - (<b[1],b[0]>/<b[0],b[0]>)*b[0]
+            old[k] = baseV[i][k];                                         //  b[2] = b[2] - (<b[2],b[0]>/<b[0],b[0]>)*b[0] - (<b[2],b[1]>/<b[1],b[1]>)*b[1]
+            baseV[i][k]= (k==i-(last_non_zero>=i?1:0)) ? 1.0f : 0.0f;     //  b[3] = b[3] - (<b[3],b[0]>/<b[0],b[0]>)*b[0] - (<b[3],b[1]>/<b[1],b[1]>)*b[1] - (<b[3],b[2]>/<b[2],b[2]>)*b[2]
+            }                                                             //  (...)
+                                                                          //  then b[i] = b[i] / |b[i]|  ( Here really b[i] = b[i] / (|b[0]|/|b[i]|)
+          tmp = baseV[i-1][k];                                            //
+          buf[i-1] += tmp*tmp;                                            //
+          }                                                               //
+                                                                          //
+        for(int j=0; j<i; j++)                                            //
+          {                                                               //
+          tmp = 0.0f;                                                     //
+          for(int k=0;k<mDimension; k++) tmp += baseV[i][k]*baseV[j][k];  //
+          tmp /= buf[j];                                                  //
+          for(int k=0;k<mDimension; k++) baseV[i][k] -= tmp*baseV[j][k];  //
+          }                                                               //
+                                                                          //
+        checkAngle(i);                                                    //
+        }                                                                 /// end compute baseV[i][*]
+
+      buf[mDimension-1]=0.0f;                                             /// Normalize
+      for(int k=0; k<mDimension; k++)                                     //
+        {                                                                 //
+        tmp = baseV[mDimension-1][k];                                     //
+        buf[mDimension-1] += tmp*tmp;                                     //
+        }                                                                 //
+                                                                          //
+      for(int i=1; i<mDimension; i++)                                     //
+        {                                                                 //
+        tmp = (float)Math.sqrt(buf[0]/buf[i]);                            //
+        for(int k=0;k<mDimension; k++) baseV[i][k] *= tmp;                //
+        }                                                                 /// End Normalize
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  abstract void interpolate(float[] buffer, int offset, float time);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 straight back to the first.
+ * </ul>
+ * 
+ * @param mode {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ */
+  public void setMode(int mode)
+    {
+    mMode = mode;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the number of Points this Dynamic has been fed with.
+ *   
+ * @return the number of Points we are currently interpolating through.
+ */
+  public synchronized int getNumPoints()
+    {
+    return numPoints;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets how many revolutions we want to do.
+ * <p>
+ * Does not have to be an integer. What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ * Count<=0 means 'go on interpolating indefinitely'.
+ * 
+ * @param count the number of times we want to interpolate between our collection of Points.
+ */
+  public void setCount(float count)
+    {
+    mCount = count;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the number of revolutions this Dynamic will make.
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @return the number revolutions this Dynamic will make.
+ */
+  public float getCount()
+    {
+    return mCount;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Start running from the beginning again.
+ *
+ * If a Dynamic has been used already, and we want to use it again and start interpolating from the
+ * first Point, first we need to reset it using this method.
+ */
+  public void resetToBeginning()
+    {
+    mStartTime = -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @param duration Number of milliseconds one revolution will take.
+ *                 What constitutes 'one revolution' depends on the MODE:
+ *                 {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ */
+  public void setDuration(long duration)
+    {
+    mDuration = duration;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @return Number of milliseconds one revolution will take.
+ */
+  public long getDuration()
+    {
+    return mDuration;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @param convexity If set to the default (1.0f) then interpolation between 4 points
+ *                  (1,0) (0,1) (-1,0) (0,-1) will be the natural circle centered at (0,0) with radius 1.
+ *                  The less it is, the less convex the circle becomes, ultimately when convexity=0.0f
+ *                  then the interpolation shape will be straight lines connecting the four points.
+ *                  Further setting this to negative values will make the shape concave.
+ *                  Valid values: all floats. (although probably only something around (0,2) actually
+ *                  makes sense)
+ */
+  public void setConvexity(float convexity)
+    {
+    if( mConvexity!=convexity )
+      {
+      mConvexity = convexity;
+      cacheDirty = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @return See {@link Dynamic#setConvexity(float)}
+ */
+  public float getConvexity()
+    {
+    return mConvexity;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the access type this Dynamic will be working in.
+ *
+ * @param type {@link Dynamic#ACCESS_TYPE_RANDOM} or {@link Dynamic#ACCESS_TYPE_SEQUENTIAL}.
+ */
+  public void setAccessType(int type)
+    {
+    mAccessType = type;
+    mLastPos = -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * @return See {@link Dynamic#setSpeedMode(int)}
+ */
+  public float getSpeedMode()
+    {
+    return mSpeedMode;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the way we compute the interpolation speed.
+ *
+ * @param mode {@link Dynamic#SPEED_MODE_SMOOTH} or {@link Dynamic#SPEED_MODE_SEGMENT_CONSTANT} or
+ *             {@link Dynamic#SPEED_MODE_GLOBALLY_CONSTANT}
+ */
+  public void setSpeedMode(int mode)
+    {
+    if( mSpeedMode!=mode )
+      {
+      if( mSpeedMode==SPEED_MODE_SMOOTH )
+        {
+        for(int i=0; i<numPoints; i++)
+          {
+          tmpCache1 = vc.elementAt(i);
+          smoothOutSegment(tmpCache1);
+          }
+        }
+
+      mSpeedMode = mode;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the Dimension, ie number of floats in a single Point this Dynamic interpolates through.
+ *
+ * @return number of floats in a single Point (ie its dimension) contained in the Dynamic.
+ */
+  public int getDimension()
+    {
+    return mDimension;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
+ * <p>
+ * This version differs from the previous in that it returns a boolean value which indicates whether
+ * the interpolation is finished.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time   Time of interpolation. Time=0.0 is the beginning of the first revolution, time=1.0 - the end
+ *               of the first revolution, time=2.5 - the middle of the third revolution.
+ *               What constitutes 'one revolution' depends on the MODE:
+ *               {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ * @param step   Time difference between now and the last time we called this function. Needed to figure
+ *               out if the previous time we were called the effect wasn't finished yet, but now it is.
+ * @return true if the interpolation reached its end.
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    if( mDuration<=0.0f )
+      {
+      interpolate(buffer,offset,mCount-(int)mCount);
+      return false;
+      }
+
+    if( mStartTime==-1 )
+      {
+      mStartTime = time;
+      mLastPos   = -1;
+      }
+
+    long diff = time-mPausedTime;
+
+    if( mStartTime<mPausedTime && mCorrectedTime<mPausedTime && diff>=0 && diff<=step )
+      {
+      mCorrectedTime = mPausedTime;
+      mStartTime += diff;
+      step -= diff;
+      }
+
+    time -= mStartTime;
+
+    if( time+step > mDuration*mCount && mCount>0.0f )
+      {
+      interpolate(buffer,offset,mCount-(int)mCount);
+      return true;
+      }
+
+    double pos;
+
+    if( mAccessType ==ACCESS_TYPE_SEQUENTIAL )
+      {
+      pos = mLastPos<0 ? (double)time/mDuration : (double)step/mDuration + mLastPos;
+      mLastPos = pos;
+      }
+    else
+      {
+      pos = (double)time/mDuration;
+      }
+
+    interpolate(buffer,offset, (float)(pos-(int)pos) );
+    return false;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/type/Dynamic1D.java b/src/main/java/org/distorted/library/type/Dynamic1D.java
deleted file mode 100644
index 7f7a7e7..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic1D.java
+++ /dev/null
@@ -1,445 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 1-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static1Ds.
-*/
-
-public class Dynamic1D extends Dynamic implements Data1D
-  {
-  private final Vector<Static1D> vv;
-  private Static1D prev, curr, next;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void computeVelocity(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);
-
-    tmpCache1 = 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 )
-        {
-        tmpCache1.velocity[0] = nx+px/q;
-        }
-      else
-        {
-        tmpCache1.velocity[0] = px+nx*q;
-        }
-      }
-    else
-      {
-      tmpCache1.velocity[0] = 0.0f;
-      }
-    }
-      
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      curr= vv.elementAt(0);
-        
-      tmpCache1.a[0] = 0.0f;
-      tmpCache1.b[0] = 0.0f;
-      tmpCache1.c[0] = curr.x;
-      tmpCache1.d[0] = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      tmpCache2 = vc.elementAt(1);
-      curr= vv.elementAt(0);
-      next= vv.elementAt(1);
-          
-      tmpCache1.a[0] = 0.0f;
-      tmpCache1.b[0] = 0.0f;
-      tmpCache1.c[0] = next.x - curr.x;
-      tmpCache1.d[0] = curr.x;
-      
-      tmpCache2.a[0] = 0.0f;
-      tmpCache2.b[0] = 0.0f;
-      tmpCache2.c[0] = curr.x - next.x;
-      tmpCache2.d[0] = next.x;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) computeVelocity(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmpCache1 = vc.elementAt(i);
-        tmpCache2 = vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-    
-        tmpCache1.cached[0] = curr.x;
-        
-        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
-        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
-        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
-        tmpCache1.d[0] = curr.x;
-
-        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Dynamic1D()
-    {
-    super(0,0.5f,1);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public Dynamic1D(int duration, float count)
-    {
-    super(duration,count,1);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static1D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static1D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */
-  public synchronized Static1D 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 Static1D 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 Static1D#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(Static1D 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 Static1D 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, Static1D 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(Static1D 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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
- * up and slowing down, etc.
- *
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-
-  public synchronized void setNoise(Static1D noise)
-    {
-    if( vn==null )
-      {
-      vn = new Vector<>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-
-      if( mDimension>=2 )
-        {
-        mFactor = new float[mDimension-1];
-        }
-
-      mNoise = new float[mDimension];
-      }
-
-    if( noise.x<0.0f ) noise.x = 0.0f;
-    if( noise.x>1.0f ) noise.x = 1.0f;
-
-    mNoise[0] = noise.x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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);
-
-              int segment= (int)(2*time);
-
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-
-              if( vn!=null )
-                {
-                if( segment != mSegment )
-                  {
-                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
-                  mSegment = segment;
-                  }
-
-                time = noise(time,0);
-                }
-             
-              buffer[offset] = (next.x-curr.x)*time + curr.x;
-              break;
-      default:computeSegmentAndTime(time);
-
-              if( mTmpVec>=0 && mTmpVec<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext = getNext(mTmpVec,time);
-                  next = vv.elementAt(vecNext);
-                  tmpCache2 = vc.elementAt(vecNext);
-
-                  if( tmpCache2.cached[0]!=next.x ) recomputeCache();
-                  }
-
-                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
-
-                mSegment = mTmpSeg;
-                time = mTmpTime-mTmpVec;
-                tmpCache1 = vc.elementAt(mTmpVec);
-                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
-
-                if( vn!=null )
-                  {
-                  time = noise(time,mTmpVec);
-                  }
-            
-                buffer[offset] = ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
-                break;
-                }
-        }
-     }  
-  
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Dynamic1D.kt b/src/main/java/org/distorted/library/type/Dynamic1D.kt
new file mode 100644
index 0000000..7f7a7e7
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic1D.kt
@@ -0,0 +1,445 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 1-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static1Ds.
+*/
+
+public class Dynamic1D extends Dynamic implements Data1D
+  {
+  private final Vector<Static1D> vv;
+  private Static1D prev, curr, next;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void computeVelocity(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);
+
+    tmpCache1 = 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 )
+        {
+        tmpCache1.velocity[0] = nx+px/q;
+        }
+      else
+        {
+        tmpCache1.velocity[0] = px+nx*q;
+        }
+      }
+    else
+      {
+      tmpCache1.velocity[0] = 0.0f;
+      }
+    }
+      
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      curr= vv.elementAt(0);
+        
+      tmpCache1.a[0] = 0.0f;
+      tmpCache1.b[0] = 0.0f;
+      tmpCache1.c[0] = curr.x;
+      tmpCache1.d[0] = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      tmpCache2 = vc.elementAt(1);
+      curr= vv.elementAt(0);
+      next= vv.elementAt(1);
+          
+      tmpCache1.a[0] = 0.0f;
+      tmpCache1.b[0] = 0.0f;
+      tmpCache1.c[0] = next.x - curr.x;
+      tmpCache1.d[0] = curr.x;
+      
+      tmpCache2.a[0] = 0.0f;
+      tmpCache2.b[0] = 0.0f;
+      tmpCache2.c[0] = curr.x - next.x;
+      tmpCache2.d[0] = next.x;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) computeVelocity(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmpCache1 = vc.elementAt(i);
+        tmpCache2 = vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+    
+        tmpCache1.cached[0] = curr.x;
+        
+        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
+        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
+        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
+        tmpCache1.d[0] = curr.x;
+
+        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Dynamic1D()
+    {
+    super(0,0.5f,1);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public Dynamic1D(int duration, float count)
+    {
+    super(duration,count,1);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static1D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static1D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */
+  public synchronized Static1D 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 Static1D 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 Static1D#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(Static1D 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 Static1D 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, Static1D 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(Static1D 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();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
+ * up and slowing down, etc.
+ *
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+
+  public synchronized void setNoise(Static1D noise)
+    {
+    if( vn==null )
+      {
+      vn = new Vector<>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+
+      if( mDimension>=2 )
+        {
+        mFactor = new float[mDimension-1];
+        }
+
+      mNoise = new float[mDimension];
+      }
+
+    if( noise.x<0.0f ) noise.x = 0.0f;
+    if( noise.x>1.0f ) noise.x = 1.0f;
+
+    mNoise[0] = noise.x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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);
+
+              int segment= (int)(2*time);
+
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+
+              if( vn!=null )
+                {
+                if( segment != mSegment )
+                  {
+                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
+                  mSegment = segment;
+                  }
+
+                time = noise(time,0);
+                }
+             
+              buffer[offset] = (next.x-curr.x)*time + curr.x;
+              break;
+      default:computeSegmentAndTime(time);
+
+              if( mTmpVec>=0 && mTmpVec<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext = getNext(mTmpVec,time);
+                  next = vv.elementAt(vecNext);
+                  tmpCache2 = vc.elementAt(vecNext);
+
+                  if( tmpCache2.cached[0]!=next.x ) recomputeCache();
+                  }
+
+                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
+
+                mSegment = mTmpSeg;
+                time = mTmpTime-mTmpVec;
+                tmpCache1 = vc.elementAt(mTmpVec);
+                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
+
+                if( vn!=null )
+                  {
+                  time = noise(time,mTmpVec);
+                  }
+            
+                buffer[offset] = ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
+                break;
+                }
+        }
+     }  
+  
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Dynamic2D.java b/src/main/java/org/distorted/library/type/Dynamic2D.java
deleted file mode 100644
index 8590611..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic2D.java
+++ /dev/null
@@ -1,487 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 2-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static2Ds.
-*/
-
-public class Dynamic2D extends Dynamic implements Data2D
-  {
-  private final Vector<Static2D> vv;
-  private Static2D prev, curr, next;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void computeVelocity(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);
-
-    tmpCache1 = 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 )
-        {
-        tmpCache1.velocity[0] = nx+px/q;
-        tmpCache1.velocity[1] = ny+py/q;
-        }
-      else
-        {
-        tmpCache1.velocity[0] = px+nx*q;
-        tmpCache1.velocity[1] = py+ny*q;
-        }
-      }
-    else
-      {
-      tmpCache1.velocity[0] = 0.0f;
-      tmpCache1.velocity[1] = 0.0f;
-      }
-    }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      curr= vv.elementAt(0);
-              
-      tmpCache1.a[0] = tmpCache1.a[1] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = 0.0f;
-      tmpCache1.c[0] = curr.x;
-      tmpCache1.c[1] = curr.y;
-      tmpCache1.d[0] = tmpCache1.d[1] = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      tmpCache2 = vc.elementAt(1);
-      curr= vv.elementAt(0);
-      next= vv.elementAt(1);
-          
-      tmpCache1.a[0] = tmpCache1.a[1] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = 0.0f;
-      tmpCache1.c[0] = next.x - curr.x;
-      tmpCache1.c[1] = next.y - curr.y;
-      tmpCache1.d[0] = curr.x;
-      tmpCache1.d[1] = curr.y;
-      
-      tmpCache2.a[0] = tmpCache2.a[1] = 0.0f;
-      tmpCache2.b[0] = tmpCache2.b[1] = 0.0f;
-      tmpCache2.c[0] = curr.x - next.x;
-      tmpCache2.c[1] = curr.y - next.y;
-      tmpCache2.d[0] = next.x;
-      tmpCache2.d[1] = next.y;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) computeVelocity(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmpCache1 = vc.elementAt(i);
-        tmpCache2 = vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-      
-        tmpCache1.cached[0] = curr.x;
-        tmpCache1.cached[1] = curr.y;
-
-        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
-        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
-        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
-        tmpCache1.d[0] = curr.x;
-
-        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
-        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
-        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
-        tmpCache1.d[1] = curr.y;
-
-        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
-        }
-      }
-    
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Dynamic2D()
-    {
-    super(0,0.5f,2);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public Dynamic2D(int duration, float count)
-    {
-    super(duration,count,2);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static2D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */  
-  public synchronized Static2D 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 Static2D 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 Static2D#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(Static2D 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 Static2D 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, Static2D 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(Static2D 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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
- * up and slowing down, etc.
- *
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-
-  public synchronized void setNoise(Static2D noise)
-    {
-    if( vn==null )
-      {
-      vn = new Vector<>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-
-      if( mDimension>=2 )
-        {
-        mFactor = new float[mDimension-1];
-        }
-
-      mNoise = new float[mDimension];
-      }
-
-    if( noise.x<0.0f ) noise.x = 0.0f;
-    if( noise.x>1.0f ) noise.x = 1.0f;
-    if( noise.y<0.0f ) noise.y = 0.0f;
-    if( noise.y>1.0f ) noise.y = 1.0f;
-
-    mNoise[0] = noise.x;
-    mNoise[1] = noise.y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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);
-
-              int segment= (int)(2*time);
-
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-
-              if( vn!=null )
-                {
-                if( segment != mSegment )
-                  {
-                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
-                  mSegment = segment;
-                  }
-
-                time = noise(time,0);
-              
-                baseV[1][0] = next.x-curr.x;
-                baseV[1][1] = next.y-curr.y;
-   
-                buffer[offset  ] = baseV[1][0]*time + curr.x +baseV[1][1]*mFactor[0];
-                buffer[offset+1] = baseV[1][1]*time + curr.y -baseV[1][0]*mFactor[0];
-                }
-              else
-                {
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
-                }
-              
-              break;
-      default:computeSegmentAndTime(time);
-
-              if( mTmpVec>=0 && mTmpVec<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext = getNext(mTmpVec,time);
-                  next = vv.elementAt(vecNext);
-                  tmpCache2 = vc.elementAt(vecNext);
-
-                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y ) recomputeCache();
-                  }
-
-                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
-
-                mSegment = mTmpSeg;
-                time = mTmpTime-mTmpVec;
-                tmpCache1 = vc.elementAt(mTmpVec);
-                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
-
-                if( vn!=null )
-                  {
-                  time = noise(time,mTmpVec);
-
-                  baseV[1][0] = (3*tmpCache1.a[0]*time + 2*tmpCache1.b[0])*time + tmpCache1.c[0];
-                  baseV[1][1] = (3*tmpCache1.a[1]*time + 2*tmpCache1.b[1])*time + tmpCache1.c[1];
-                 
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] +baseV[1][1]*mFactor[0];
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] -baseV[1][0]*mFactor[0];
-                  } 
-                else
-                  {
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
-                  }
-                
-                break;
-                }
-      }
-    }  
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Dynamic2D.kt b/src/main/java/org/distorted/library/type/Dynamic2D.kt
new file mode 100644
index 0000000..8590611
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic2D.kt
@@ -0,0 +1,487 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 2-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static2Ds.
+*/
+
+public class Dynamic2D extends Dynamic implements Data2D
+  {
+  private final Vector<Static2D> vv;
+  private Static2D prev, curr, next;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void computeVelocity(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);
+
+    tmpCache1 = 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 )
+        {
+        tmpCache1.velocity[0] = nx+px/q;
+        tmpCache1.velocity[1] = ny+py/q;
+        }
+      else
+        {
+        tmpCache1.velocity[0] = px+nx*q;
+        tmpCache1.velocity[1] = py+ny*q;
+        }
+      }
+    else
+      {
+      tmpCache1.velocity[0] = 0.0f;
+      tmpCache1.velocity[1] = 0.0f;
+      }
+    }
+   
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      curr= vv.elementAt(0);
+              
+      tmpCache1.a[0] = tmpCache1.a[1] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = 0.0f;
+      tmpCache1.c[0] = curr.x;
+      tmpCache1.c[1] = curr.y;
+      tmpCache1.d[0] = tmpCache1.d[1] = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      tmpCache2 = vc.elementAt(1);
+      curr= vv.elementAt(0);
+      next= vv.elementAt(1);
+          
+      tmpCache1.a[0] = tmpCache1.a[1] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = 0.0f;
+      tmpCache1.c[0] = next.x - curr.x;
+      tmpCache1.c[1] = next.y - curr.y;
+      tmpCache1.d[0] = curr.x;
+      tmpCache1.d[1] = curr.y;
+      
+      tmpCache2.a[0] = tmpCache2.a[1] = 0.0f;
+      tmpCache2.b[0] = tmpCache2.b[1] = 0.0f;
+      tmpCache2.c[0] = curr.x - next.x;
+      tmpCache2.c[1] = curr.y - next.y;
+      tmpCache2.d[0] = next.x;
+      tmpCache2.d[1] = next.y;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) computeVelocity(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmpCache1 = vc.elementAt(i);
+        tmpCache2 = vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+      
+        tmpCache1.cached[0] = curr.x;
+        tmpCache1.cached[1] = curr.y;
+
+        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
+        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
+        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
+        tmpCache1.d[0] = curr.x;
+
+        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
+        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
+        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
+        tmpCache1.d[1] = curr.y;
+
+        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
+        }
+      }
+    
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Dynamic2D()
+    {
+    super(0,0.5f,2);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public Dynamic2D(int duration, float count)
+    {
+    super(duration,count,2);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static2D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static2D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */  
+  public synchronized Static2D 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 Static2D 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 Static2D#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(Static2D 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 Static2D 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, Static2D 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(Static2D 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();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
+ * up and slowing down, etc.
+ *
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+
+  public synchronized void setNoise(Static2D noise)
+    {
+    if( vn==null )
+      {
+      vn = new Vector<>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+
+      if( mDimension>=2 )
+        {
+        mFactor = new float[mDimension-1];
+        }
+
+      mNoise = new float[mDimension];
+      }
+
+    if( noise.x<0.0f ) noise.x = 0.0f;
+    if( noise.x>1.0f ) noise.x = 1.0f;
+    if( noise.y<0.0f ) noise.y = 0.0f;
+    if( noise.y>1.0f ) noise.y = 1.0f;
+
+    mNoise[0] = noise.x;
+    mNoise[1] = noise.y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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);
+
+              int segment= (int)(2*time);
+
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+
+              if( vn!=null )
+                {
+                if( segment != mSegment )
+                  {
+                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
+                  mSegment = segment;
+                  }
+
+                time = noise(time,0);
+              
+                baseV[1][0] = next.x-curr.x;
+                baseV[1][1] = next.y-curr.y;
+   
+                buffer[offset  ] = baseV[1][0]*time + curr.x +baseV[1][1]*mFactor[0];
+                buffer[offset+1] = baseV[1][1]*time + curr.y -baseV[1][0]*mFactor[0];
+                }
+              else
+                {
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
+                }
+              
+              break;
+      default:computeSegmentAndTime(time);
+
+              if( mTmpVec>=0 && mTmpVec<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext = getNext(mTmpVec,time);
+                  next = vv.elementAt(vecNext);
+                  tmpCache2 = vc.elementAt(vecNext);
+
+                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y ) recomputeCache();
+                  }
+
+                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
+
+                mSegment = mTmpSeg;
+                time = mTmpTime-mTmpVec;
+                tmpCache1 = vc.elementAt(mTmpVec);
+                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
+
+                if( vn!=null )
+                  {
+                  time = noise(time,mTmpVec);
+
+                  baseV[1][0] = (3*tmpCache1.a[0]*time + 2*tmpCache1.b[0])*time + tmpCache1.c[0];
+                  baseV[1][1] = (3*tmpCache1.a[1]*time + 2*tmpCache1.b[1])*time + tmpCache1.c[1];
+                 
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] +baseV[1][1]*mFactor[0];
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] -baseV[1][0]*mFactor[0];
+                  } 
+                else
+                  {
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
+                  }
+                
+                break;
+                }
+      }
+    }  
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Dynamic3D.java b/src/main/java/org/distorted/library/type/Dynamic3D.java
deleted file mode 100644
index ffd0256..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic3D.java
+++ /dev/null
@@ -1,513 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 3-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static3Ds.
-*/
-
-public class Dynamic3D extends Dynamic implements Data3D
-  {
-  private final Vector<Static3D> vv;
-  private Static3D prev, curr, next;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void computeVelocity(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);
-
-    tmpCache1 = 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 )
-        {
-        tmpCache1.velocity[0] = nx+px/q;
-        tmpCache1.velocity[1] = ny+py/q;
-        tmpCache1.velocity[2] = nz+pz/q;
-        }
-      else
-        {
-        tmpCache1.velocity[0] = px+nx*q;
-        tmpCache1.velocity[1] = py+ny*q;
-        tmpCache1.velocity[2] = pz+nz*q;
-        }
-      }
-    else
-      {
-      tmpCache1.velocity[0] = 0.0f;
-      tmpCache1.velocity[1] = 0.0f;
-      tmpCache1.velocity[2] = 0.0f;
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      curr= vv.elementAt(0);
-        
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = 0.0f;
-      tmpCache1.c[0] = curr.x;
-      tmpCache1.c[1] = curr.y;
-      tmpCache1.c[2] = curr.z;
-      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[2] = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      tmpCache2 = vc.elementAt(1);
-      curr= vv.elementAt(0);
-      next= vv.elementAt(1);
-          
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = 0.0f;
-      tmpCache1.c[0] = next.x - curr.x;
-      tmpCache1.c[1] = next.y - curr.y;
-      tmpCache1.c[2] = next.z - curr.z;
-      tmpCache1.d[0] = curr.x;
-      tmpCache1.d[1] = curr.y;
-      tmpCache1.d[2] = curr.z;
-      
-      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = 0.0f;
-      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = 0.0f;
-      tmpCache2.c[0] = curr.x - next.x;
-      tmpCache2.c[1] = curr.y - next.y;
-      tmpCache2.c[2] = curr.z - next.z;
-      tmpCache2.d[0] = next.x;
-      tmpCache2.d[1] = next.y;
-      tmpCache2.d[2] = next.z;
-      }
-    else
-      {
-      int i, n;  
-         
-      for(i=0; i<numPoints; i++) computeVelocity(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmpCache1 = vc.elementAt(i);
-        tmpCache2 = vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-      
-        tmpCache1.cached[0] = curr.x;
-        tmpCache1.cached[1] = curr.y;
-        tmpCache1.cached[2] = curr.z;
-        
-        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
-        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
-        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
-        tmpCache1.d[0] = curr.x;
-      
-        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
-        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
-        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
-        tmpCache1.d[1] = curr.y;
-      
-        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
-        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
-        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
-        tmpCache1.d[2] = curr.z;
-
-        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Dynamic3D()
-    {
-    super(0,0.5f,3);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public Dynamic3D(int duration, float count)
-    {
-    super(duration,count,3);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static3D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static3D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */  
-  public synchronized Static3D 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 Static3D 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 Static3D#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(Static3D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-        
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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 Static3D 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, Static3D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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(Static3D 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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
- * up and slowing down, etc.
- *
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-
-  public synchronized void setNoise(Static3D noise)
-    {
-    if( vn==null )
-      {
-      vn = new Vector<>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-
-      if( mDimension>=2 )
-        {
-        mFactor = new float[mDimension-1];
-        }
-
-      mNoise = new float[mDimension];
-      }
-
-    if( noise.x<0.0f ) noise.x = 0.0f;
-    if( noise.x>1.0f ) noise.x = 1.0f;
-    if( noise.y<0.0f ) noise.y = 0.0f;
-    if( noise.y>1.0f ) noise.y = 1.0f;
-    if( noise.z<0.0f ) noise.z = 0.0f;
-    if( noise.z>1.0f ) noise.z = 1.0f;
-
-    mNoise[0] = noise.x;
-    mNoise[1] = noise.y;
-    mNoise[2] = noise.z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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);
-
-              int segment= (int)(2*time);
-
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-
-              if( vn!=null )
-                {
-                if( segment != mSegment )
-                  {
-                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
-                  mSegment = segment;
-                  }
-
-                time = noise(time,0);
-            
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
-                }
-              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:computeSegmentAndTime(time);
-
-              if( mTmpVec>=0 && mTmpVec<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext = getNext(mTmpVec,time);
-                  next = vv.elementAt(vecNext);
-                  tmpCache2 = vc.elementAt(vecNext);
-
-                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z ) recomputeCache();
-                  }
-
-                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
-
-                mSegment = mTmpSeg;
-                time = mTmpTime-mTmpVec;
-                tmpCache1 = vc.elementAt(mTmpVec);
-                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
-
-                if( vn!=null )
-                  {
-                  time = noise(time,mTmpVec);
-              
-                  computeOrthonormalBaseMore(time, tmpCache1);
-                 
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
-                  }
-                else
-                  {
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
-                  }
-               
-                break;
-                }
-       }
-     }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Dynamic3D.kt b/src/main/java/org/distorted/library/type/Dynamic3D.kt
new file mode 100644
index 0000000..ffd0256
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic3D.kt
@@ -0,0 +1,513 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 3-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static3Ds.
+*/
+
+public class Dynamic3D extends Dynamic implements Data3D
+  {
+  private final Vector<Static3D> vv;
+  private Static3D prev, curr, next;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void computeVelocity(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);
+
+    tmpCache1 = 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 )
+        {
+        tmpCache1.velocity[0] = nx+px/q;
+        tmpCache1.velocity[1] = ny+py/q;
+        tmpCache1.velocity[2] = nz+pz/q;
+        }
+      else
+        {
+        tmpCache1.velocity[0] = px+nx*q;
+        tmpCache1.velocity[1] = py+ny*q;
+        tmpCache1.velocity[2] = pz+nz*q;
+        }
+      }
+    else
+      {
+      tmpCache1.velocity[0] = 0.0f;
+      tmpCache1.velocity[1] = 0.0f;
+      tmpCache1.velocity[2] = 0.0f;
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      curr= vv.elementAt(0);
+        
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = 0.0f;
+      tmpCache1.c[0] = curr.x;
+      tmpCache1.c[1] = curr.y;
+      tmpCache1.c[2] = curr.z;
+      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[2] = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      tmpCache2 = vc.elementAt(1);
+      curr= vv.elementAt(0);
+      next= vv.elementAt(1);
+          
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = 0.0f;
+      tmpCache1.c[0] = next.x - curr.x;
+      tmpCache1.c[1] = next.y - curr.y;
+      tmpCache1.c[2] = next.z - curr.z;
+      tmpCache1.d[0] = curr.x;
+      tmpCache1.d[1] = curr.y;
+      tmpCache1.d[2] = curr.z;
+      
+      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = 0.0f;
+      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = 0.0f;
+      tmpCache2.c[0] = curr.x - next.x;
+      tmpCache2.c[1] = curr.y - next.y;
+      tmpCache2.c[2] = curr.z - next.z;
+      tmpCache2.d[0] = next.x;
+      tmpCache2.d[1] = next.y;
+      tmpCache2.d[2] = next.z;
+      }
+    else
+      {
+      int i, n;  
+         
+      for(i=0; i<numPoints; i++) computeVelocity(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmpCache1 = vc.elementAt(i);
+        tmpCache2 = vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+      
+        tmpCache1.cached[0] = curr.x;
+        tmpCache1.cached[1] = curr.y;
+        tmpCache1.cached[2] = curr.z;
+        
+        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
+        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
+        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
+        tmpCache1.d[0] = curr.x;
+      
+        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
+        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
+        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
+        tmpCache1.d[1] = curr.y;
+      
+        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
+        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
+        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
+        tmpCache1.d[2] = curr.z;
+
+        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Dynamic3D()
+    {
+    super(0,0.5f,3);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public Dynamic3D(int duration, float count)
+    {
+    super(duration,count,3);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static3D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static3D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */  
+  public synchronized Static3D 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 Static3D 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 Static3D#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(Static3D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+        
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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 Static3D 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, Static3D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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(Static3D 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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
+ * up and slowing down, etc.
+ *
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+
+  public synchronized void setNoise(Static3D noise)
+    {
+    if( vn==null )
+      {
+      vn = new Vector<>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+
+      if( mDimension>=2 )
+        {
+        mFactor = new float[mDimension-1];
+        }
+
+      mNoise = new float[mDimension];
+      }
+
+    if( noise.x<0.0f ) noise.x = 0.0f;
+    if( noise.x>1.0f ) noise.x = 1.0f;
+    if( noise.y<0.0f ) noise.y = 0.0f;
+    if( noise.y>1.0f ) noise.y = 1.0f;
+    if( noise.z<0.0f ) noise.z = 0.0f;
+    if( noise.z>1.0f ) noise.z = 1.0f;
+
+    mNoise[0] = noise.x;
+    mNoise[1] = noise.y;
+    mNoise[2] = noise.z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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);
+
+              int segment= (int)(2*time);
+
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+
+              if( vn!=null )
+                {
+                if( segment != mSegment )
+                  {
+                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
+                  mSegment = segment;
+                  }
+
+                time = noise(time,0);
+            
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
+                }
+              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:computeSegmentAndTime(time);
+
+              if( mTmpVec>=0 && mTmpVec<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext = getNext(mTmpVec,time);
+                  next = vv.elementAt(vecNext);
+                  tmpCache2 = vc.elementAt(vecNext);
+
+                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z ) recomputeCache();
+                  }
+
+                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
+
+                mSegment = mTmpSeg;
+                time = mTmpTime-mTmpVec;
+                tmpCache1 = vc.elementAt(mTmpVec);
+                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
+
+                if( vn!=null )
+                  {
+                  time = noise(time,mTmpVec);
+              
+                  computeOrthonormalBaseMore(time, tmpCache1);
+                 
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
+                  }
+                else
+                  {
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
+                  }
+               
+                break;
+                }
+       }
+     }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Dynamic4D.java b/src/main/java/org/distorted/library/type/Dynamic4D.java
deleted file mode 100644
index 4716d34..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic4D.java
+++ /dev/null
@@ -1,538 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 4-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static4Ds.
-*/
-
-public class Dynamic4D extends Dynamic implements Data4D
-  {
-  private final Vector<Static4D> vv;
-  private Static4D prev, curr, next;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void computeVelocity(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);
-
-    tmpCache1 = 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 )
-        {
-        tmpCache1.velocity[0] = nx+px/q;
-        tmpCache1.velocity[1] = ny+py/q;
-        tmpCache1.velocity[2] = nz+pz/q;
-        tmpCache1.velocity[3] = nw+pw/q;
-        }
-      else
-        {
-        tmpCache1.velocity[0] = px+nx*q;
-        tmpCache1.velocity[1] = py+ny*q;
-        tmpCache1.velocity[2] = pz+nz*q;
-        tmpCache1.velocity[3] = pw+nw*q;
-        }
-      }
-    else
-      {
-      tmpCache1.velocity[0] = 0.0f;
-      tmpCache1.velocity[1] = 0.0f;
-      tmpCache1.velocity[2] = 0.0f;
-      tmpCache1.velocity[3] = 0.0f;
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      curr= vv.elementAt(0);
-        
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
-      tmpCache1.c[0] = curr.x;
-      tmpCache1.c[1] = curr.y;
-      tmpCache1.c[2] = curr.z;
-      tmpCache1.c[3] = curr.w;
-      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[3] = tmpCache1.d[3] = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      tmpCache2 = vc.elementAt(1);
-      curr= vv.elementAt(0);
-      next= vv.elementAt(1);
-      
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
-      tmpCache1.c[0] = next.x - curr.x;
-      tmpCache1.c[1] = next.y - curr.y;
-      tmpCache1.c[2] = next.z - curr.z;
-      tmpCache1.c[3] = next.w - curr.w;
-      tmpCache1.d[0] = curr.x;
-      tmpCache1.d[1] = curr.y;
-      tmpCache1.d[2] = curr.z;
-      tmpCache1.d[3] = curr.w;
-      
-      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = tmpCache2.a[3] = 0.0f;
-      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = tmpCache2.b[3] = 0.0f;
-      tmpCache2.c[0] = curr.x - next.x;
-      tmpCache2.c[1] = curr.y - next.y;
-      tmpCache2.c[2] = curr.z - next.z;
-      tmpCache2.c[3] = curr.w - next.w;
-      tmpCache2.d[0] = next.x;
-      tmpCache2.d[1] = next.y;
-      tmpCache2.d[2] = next.z;
-      tmpCache2.d[3] = next.w;
-      }
-    else
-      {
-      int i, n;  
-      
-      for(i=0; i<numPoints; i++) computeVelocity(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmpCache1 = vc.elementAt(i);
-        tmpCache2 = vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-      
-        tmpCache1.cached[0] = curr.x;
-        tmpCache1.cached[1] = curr.y;
-        tmpCache1.cached[2] = curr.z;
-        tmpCache1.cached[3] = curr.w;
-        
-        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
-        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
-        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
-        tmpCache1.d[0] = curr.x;
-      
-        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
-        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
-        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
-        tmpCache1.d[1] = curr.y;
-      
-        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
-        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
-        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
-        tmpCache1.d[2] = curr.z;
-        
-        tmpCache1.a[3] = mConvexity*( 2*curr.w +   tmpCache1.velocity[3] - 2*next.w + tmpCache2.velocity[3]);
-        tmpCache1.b[3] = mConvexity*(-3*curr.w - 2* tmpCache1.velocity[3] + 3*next.w - tmpCache2.velocity[3]);
-        tmpCache1.c[3] = mConvexity*(tmpCache1.velocity[3]) + (1.0f-mConvexity)*(next.w-curr.w);
-        tmpCache1.d[3] = curr.w;
-
-        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Dynamic4D()
-    {
-    super(0,0.5f,4);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public Dynamic4D(int duration, float count)
-    {
-    super(duration,count,4);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static4D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */  
-  public synchronized Static4D 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 Static4D 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 Static4D#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(Static4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-        
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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 Static4D 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 Static4D to add.
- */  
-  public synchronized void add(int location, Static4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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(Static4D 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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
- * up and slowing down, etc.
- *
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-
-  public synchronized void setNoise(Static4D noise)
-    {
-    if( vn==null )
-      {
-      vn = new Vector<>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-
-      if( mDimension>=2 )
-        {
-        mFactor = new float[mDimension-1];
-        }
-
-      mNoise = new float[mDimension];
-      }
-
-    if( noise.x<0.0f ) noise.x = 0.0f;
-    if( noise.x>1.0f ) noise.x = 1.0f;
-    if( noise.y<0.0f ) noise.y = 0.0f;
-    if( noise.y>1.0f ) noise.y = 1.0f;
-    if( noise.z<0.0f ) noise.z = 0.0f;
-    if( noise.z>1.0f ) noise.z = 1.0f;
-    if( noise.w<0.0f ) noise.w = 0.0f;
-    if( noise.w>1.0f ) noise.w = 1.0f;
-
-    mNoise[0] = noise.x;
-    mNoise[1] = noise.y;
-    mNoise[2] = noise.z;
-    mNoise[3] = noise.w;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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);
-
-              int segment= (int)(2*time);
-
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-
-              if( vn!=null )
-                {
-                if( segment != mSegment )
-                  {
-                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
-                  mSegment = segment;
-                  }
-
-                time = noise(time,0);
-
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
-                }
-              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:computeSegmentAndTime(time);
-
-              if( mTmpVec>=0 && mTmpVec<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext = getNext(mTmpVec,time);
-                  next = vv.elementAt(vecNext);
-                  tmpCache2 = vc.elementAt(vecNext);
-
-                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z || tmpCache2.cached[3]!=next.w ) recomputeCache();
-                  }
-
-                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
-
-                mSegment = mTmpSeg;
-                time = mTmpTime-mTmpVec;
-                tmpCache1 = vc.elementAt(mTmpVec);
-                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
-
-                if( vn!=null )
-                  {
-                  time = noise(time,mTmpVec);
-              
-                  computeOrthonormalBaseMore(time, tmpCache1);
-
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
-                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
-                  }
-                else
-                  {
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
-                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
-                  }
-
-                break;
-                }
-      }
-    }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Dynamic4D.kt b/src/main/java/org/distorted/library/type/Dynamic4D.kt
new file mode 100644
index 0000000..4716d34
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic4D.kt
@@ -0,0 +1,538 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 4-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static4Ds.
+*/
+
+public class Dynamic4D extends Dynamic implements Data4D
+  {
+  private final Vector<Static4D> vv;
+  private Static4D prev, curr, next;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void computeVelocity(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);
+
+    tmpCache1 = 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 )
+        {
+        tmpCache1.velocity[0] = nx+px/q;
+        tmpCache1.velocity[1] = ny+py/q;
+        tmpCache1.velocity[2] = nz+pz/q;
+        tmpCache1.velocity[3] = nw+pw/q;
+        }
+      else
+        {
+        tmpCache1.velocity[0] = px+nx*q;
+        tmpCache1.velocity[1] = py+ny*q;
+        tmpCache1.velocity[2] = pz+nz*q;
+        tmpCache1.velocity[3] = pw+nw*q;
+        }
+      }
+    else
+      {
+      tmpCache1.velocity[0] = 0.0f;
+      tmpCache1.velocity[1] = 0.0f;
+      tmpCache1.velocity[2] = 0.0f;
+      tmpCache1.velocity[3] = 0.0f;
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      curr= vv.elementAt(0);
+        
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
+      tmpCache1.c[0] = curr.x;
+      tmpCache1.c[1] = curr.y;
+      tmpCache1.c[2] = curr.z;
+      tmpCache1.c[3] = curr.w;
+      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[3] = tmpCache1.d[3] = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      tmpCache2 = vc.elementAt(1);
+      curr= vv.elementAt(0);
+      next= vv.elementAt(1);
+      
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = 0.0f;
+      tmpCache1.c[0] = next.x - curr.x;
+      tmpCache1.c[1] = next.y - curr.y;
+      tmpCache1.c[2] = next.z - curr.z;
+      tmpCache1.c[3] = next.w - curr.w;
+      tmpCache1.d[0] = curr.x;
+      tmpCache1.d[1] = curr.y;
+      tmpCache1.d[2] = curr.z;
+      tmpCache1.d[3] = curr.w;
+      
+      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = tmpCache2.a[3] = 0.0f;
+      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = tmpCache2.b[3] = 0.0f;
+      tmpCache2.c[0] = curr.x - next.x;
+      tmpCache2.c[1] = curr.y - next.y;
+      tmpCache2.c[2] = curr.z - next.z;
+      tmpCache2.c[3] = curr.w - next.w;
+      tmpCache2.d[0] = next.x;
+      tmpCache2.d[1] = next.y;
+      tmpCache2.d[2] = next.z;
+      tmpCache2.d[3] = next.w;
+      }
+    else
+      {
+      int i, n;  
+      
+      for(i=0; i<numPoints; i++) computeVelocity(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmpCache1 = vc.elementAt(i);
+        tmpCache2 = vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+      
+        tmpCache1.cached[0] = curr.x;
+        tmpCache1.cached[1] = curr.y;
+        tmpCache1.cached[2] = curr.z;
+        tmpCache1.cached[3] = curr.w;
+        
+        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
+        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
+        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
+        tmpCache1.d[0] = curr.x;
+      
+        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
+        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
+        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
+        tmpCache1.d[1] = curr.y;
+      
+        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
+        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
+        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
+        tmpCache1.d[2] = curr.z;
+        
+        tmpCache1.a[3] = mConvexity*( 2*curr.w +   tmpCache1.velocity[3] - 2*next.w + tmpCache2.velocity[3]);
+        tmpCache1.b[3] = mConvexity*(-3*curr.w - 2* tmpCache1.velocity[3] + 3*next.w - tmpCache2.velocity[3]);
+        tmpCache1.c[3] = mConvexity*(tmpCache1.velocity[3]) + (1.0f-mConvexity)*(next.w-curr.w);
+        tmpCache1.d[3] = curr.w;
+
+        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Dynamic4D()
+    {
+    super(0,0.5f,4);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public Dynamic4D(int duration, float count)
+    {
+    super(duration,count,4);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static4D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */  
+  public synchronized Static4D 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 Static4D 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 Static4D#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(Static4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+        
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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 Static4D 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 Static4D to add.
+ */  
+  public synchronized void add(int location, Static4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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(Static4D 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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
+ * up and slowing down, etc.
+ *
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+
+  public synchronized void setNoise(Static4D noise)
+    {
+    if( vn==null )
+      {
+      vn = new Vector<>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+
+      if( mDimension>=2 )
+        {
+        mFactor = new float[mDimension-1];
+        }
+
+      mNoise = new float[mDimension];
+      }
+
+    if( noise.x<0.0f ) noise.x = 0.0f;
+    if( noise.x>1.0f ) noise.x = 1.0f;
+    if( noise.y<0.0f ) noise.y = 0.0f;
+    if( noise.y>1.0f ) noise.y = 1.0f;
+    if( noise.z<0.0f ) noise.z = 0.0f;
+    if( noise.z>1.0f ) noise.z = 1.0f;
+    if( noise.w<0.0f ) noise.w = 0.0f;
+    if( noise.w>1.0f ) noise.w = 1.0f;
+
+    mNoise[0] = noise.x;
+    mNoise[1] = noise.y;
+    mNoise[2] = noise.z;
+    mNoise[3] = noise.w;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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);
+
+              int segment= (int)(2*time);
+
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+
+              if( vn!=null )
+                {
+                if( segment != mSegment )
+                  {
+                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
+                  mSegment = segment;
+                  }
+
+                time = noise(time,0);
+
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
+                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
+                }
+              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:computeSegmentAndTime(time);
+
+              if( mTmpVec>=0 && mTmpVec<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext = getNext(mTmpVec,time);
+                  next = vv.elementAt(vecNext);
+                  tmpCache2 = vc.elementAt(vecNext);
+
+                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z || tmpCache2.cached[3]!=next.w ) recomputeCache();
+                  }
+
+                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
+
+                mSegment = mTmpSeg;
+                time = mTmpTime-mTmpVec;
+                tmpCache1 = vc.elementAt(mTmpVec);
+                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
+
+                if( vn!=null )
+                  {
+                  time = noise(time,mTmpVec);
+              
+                  computeOrthonormalBaseMore(time, tmpCache1);
+
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
+                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
+                  }
+                else
+                  {
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
+                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
+                  }
+
+                break;
+                }
+      }
+    }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/Dynamic5D.java b/src/main/java/org/distorted/library/type/Dynamic5D.java
deleted file mode 100644
index 3e8a109..0000000
--- a/src/main/java/org/distorted/library/type/Dynamic5D.java
+++ /dev/null
@@ -1,563 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 5-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static5Ds.
-*/
-
-public class Dynamic5D extends Dynamic implements Data5D
-  {
-  private final Vector<Static5D> vv;
-  private Static5D prev, curr, next;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// no array bounds checking!
-  
-  private void computeVelocity(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);
-
-    tmpCache1 = 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 pv = curr.v - prev.v;
-    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 nv = next.v - curr.v;
-     
-    float d = nx*nx+ny*ny+nz*nz+nw*nw+nv*nv;
-    
-    if( d>0 )
-      {
-      float q = (float)Math.sqrt((px*px+py*py+pz*pz+pw*pw+pv*pv)/d);
-      
-      if( q>1 )
-        {
-        tmpCache1.velocity[0] = nx+px/q;
-        tmpCache1.velocity[1] = ny+py/q;
-        tmpCache1.velocity[2] = nz+pz/q;
-        tmpCache1.velocity[3] = nw+pw/q;
-        tmpCache1.velocity[4] = nv+pv/q;
-        }
-      else
-        {
-        tmpCache1.velocity[0] = px+nx*q;
-        tmpCache1.velocity[1] = py+ny*q;
-        tmpCache1.velocity[2] = pz+nz*q;
-        tmpCache1.velocity[3] = pw+nw*q;
-        tmpCache1.velocity[4] = pv+nv*q;
-        }
-      }
-    else
-      {
-      tmpCache1.velocity[0] = 0.0f;
-      tmpCache1.velocity[1] = 0.0f;
-      tmpCache1.velocity[2] = 0.0f;
-      tmpCache1.velocity[3] = 0.0f;
-      tmpCache1.velocity[4] = 0.0f;
-      }
-    }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private void recomputeCache()
-    {  
-    if( numPoints==1 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      curr= vv.elementAt(0);
-        
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = tmpCache1.a[4] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = tmpCache1.b[4] = 0.0f;
-      tmpCache1.c[0] = curr.x;
-      tmpCache1.c[1] = curr.y;
-      tmpCache1.c[2] = curr.z;
-      tmpCache1.c[3] = curr.w;
-      tmpCache1.c[4] = curr.v;
-      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[2] = tmpCache1.d[3] = tmpCache1.d[4] = 0.0f;
-      }
-    else if( numPoints==2 )
-      {
-      tmpCache1 = vc.elementAt(0);
-      tmpCache2 = vc.elementAt(1);
-      curr= vv.elementAt(0);
-      next= vv.elementAt(1);
-      
-      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = tmpCache1.a[4] = 0.0f;
-      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = tmpCache1.b[4] = 0.0f;
-      tmpCache1.c[0] = next.x - curr.x;
-      tmpCache1.c[1] = next.y - curr.y;
-      tmpCache1.c[2] = next.z - curr.z;
-      tmpCache1.c[3] = next.w - curr.w;
-      tmpCache1.c[4] = next.v - curr.v;
-      tmpCache1.d[0] = curr.x;
-      tmpCache1.d[1] = curr.y;
-      tmpCache1.d[2] = curr.z;
-      tmpCache1.d[3] = curr.w;
-      tmpCache1.d[4] = curr.v;
-      
-      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = tmpCache2.a[3] = tmpCache2.a[4] = 0.0f;
-      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = tmpCache2.b[3] = tmpCache2.b[4] = 0.0f;
-      tmpCache2.c[0] = curr.x - next.x;
-      tmpCache2.c[1] = curr.y - next.y;
-      tmpCache2.c[2] = curr.z - next.z;
-      tmpCache2.c[3] = curr.w - next.w;
-      tmpCache2.c[4] = curr.v - next.v;
-      tmpCache2.d[0] = next.x;
-      tmpCache2.d[1] = next.y;
-      tmpCache2.d[2] = next.z;
-      tmpCache2.d[3] = next.w;
-      tmpCache2.d[4] = next.v;
-      }
-    else
-      {
-      int i, n;  
-      
-      for(i=0; i<numPoints; i++) computeVelocity(i);
-   
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        tmpCache1 = vc.elementAt(i);
-        tmpCache2 = vc.elementAt(n);
-        curr= vv.elementAt(i);
-        next= vv.elementAt(n);
-      
-        tmpCache1.cached[0] = curr.x;
-        tmpCache1.cached[1] = curr.y;
-        tmpCache1.cached[2] = curr.z;
-        tmpCache1.cached[3] = curr.w;
-        tmpCache1.cached[4] = curr.v;
-        
-        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
-        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
-        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
-        tmpCache1.d[0] = curr.x;
-      
-        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
-        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
-        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
-        tmpCache1.d[1] = curr.y;
-      
-        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
-        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
-        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
-        tmpCache1.d[2] = curr.z;
-        
-        tmpCache1.a[3] = mConvexity*( 2*curr.w +   tmpCache1.velocity[3] - 2*next.w + tmpCache2.velocity[3]);
-        tmpCache1.b[3] = mConvexity*(-3*curr.w - 2* tmpCache1.velocity[3] + 3*next.w - tmpCache2.velocity[3]);
-        tmpCache1.c[3] = mConvexity*(tmpCache1.velocity[3]) + (1.0f-mConvexity)*(next.w-curr.w);
-        tmpCache1.d[3] = curr.w;
-        
-        tmpCache1.a[4] = mConvexity*( 2*curr.v +   tmpCache1.velocity[4] - 2*next.v + tmpCache2.velocity[4]);
-        tmpCache1.b[4] = mConvexity*(-3*curr.v - 2* tmpCache1.velocity[4] + 3*next.v - tmpCache2.velocity[4]);
-        tmpCache1.c[4] = mConvexity*(tmpCache1.velocity[4]) + (1.0f-mConvexity)*(next.v-curr.v);
-        tmpCache1.d[4] = curr.v;
-
-        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public Dynamic5D()
-    {
-    super(0,0.5f,5);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public Dynamic5D(int duration, float count)
-    {
-    super(duration,count,5);
-    vv = new Vector<>();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static5D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static5D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */  
-  public synchronized Static5D 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, float v)
-    {
-    if( location>=0 && location<numPoints )
-      {
-      curr = vv.elementAt(location);
-   
-      if( curr!=null )
-        {
-        curr.set(x,y,z,w,v);
-        cacheDirty=true;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Static5D 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 Static5D#set(float,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(Static5D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-        
-      if( vn!=null ) vn.add(new VectorNoise());
-       
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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 Static5D 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 Static5D to add.
- */  
-  public synchronized void add(int location, Static5D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      if( vn!=null ) vn.add(new VectorNoise());
-      
-      switch(numPoints)
-        {
-        case 0: break;
-        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
-                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(Static5D 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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
-                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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
- * up and slowing down, etc.
- *
- * @param noise The noise level. Permitted range: 0 <= noise <= 1.
- */
-
-  public synchronized void setNoise(Static5D noise)
-    {
-    if( vn==null )
-      {
-      vn = new Vector<>();
-      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
-
-      if( mDimension>=2 )
-        {
-        mFactor = new float[mDimension-1];
-        }
-
-      mNoise = new float[mDimension];
-      }
-
-    if( noise.x<0.0f ) noise.x = 0.0f;
-    if( noise.x>1.0f ) noise.x = 1.0f;
-    if( noise.y<0.0f ) noise.y = 0.0f;
-    if( noise.y>1.0f ) noise.y = 1.0f;
-    if( noise.z<0.0f ) noise.z = 0.0f;
-    if( noise.z>1.0f ) noise.z = 1.0f;
-    if( noise.w<0.0f ) noise.w = 0.0f;
-    if( noise.w>1.0f ) noise.w = 1.0f;
-    if( noise.v<0.0f ) noise.v = 0.0f;
-    if( noise.v>1.0f ) noise.v = 1.0f;
-
-    mNoise[0] = noise.x;
-    mNoise[1] = noise.y;
-    mNoise[2] = noise.z;
-    mNoise[3] = noise.w;
-    mNoise[4] = noise.v;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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;
-              buffer[offset+4] = 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;
-              buffer[offset+4] = curr.v;
-              break;
-      case 2: curr = vv.elementAt(0);
-              next = vv.elementAt(1);
-
-              int segment= (int)(2*time);
-
-              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
-
-              if( vn!=null )
-                {
-                if( segment != mSegment )
-                  {
-                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
-                  mSegment = segment;
-                  }
-
-                time = noise(time,0);
-
-                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2] + baseV[4][0]*mFactor[3]);
-                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2] + baseV[4][1]*mFactor[3]);
-                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2] + baseV[4][2]*mFactor[3]);
-                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2] + baseV[4][3]*mFactor[3]);
-                buffer[offset+4] = (next.v-curr.v)*time + curr.v + (baseV[1][4]*mFactor[0] + baseV[2][4]*mFactor[1] + baseV[3][4]*mFactor[2] + baseV[4][4]*mFactor[3]);
-                }
-              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;
-                buffer[offset+4] = (next.v-curr.v)*time + curr.v;
-                }
-                
-              break;
-      default:computeSegmentAndTime(time);
-
-              if( mTmpVec>=0 && mTmpVec<numPoints )
-                {
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  int vecNext = getNext(mTmpVec,time);
-                  next = vv.elementAt(vecNext);
-                  tmpCache2 = vc.elementAt(vecNext);
-
-                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z || tmpCache2.cached[3]!=next.w || tmpCache2.cached[4]!=next.v ) recomputeCache();
-                  }
-
-                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
-
-                mSegment = mTmpSeg;
-                time = mTmpTime-mTmpVec;
-                tmpCache1 = vc.elementAt(mTmpVec);
-                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
-
-                if( vn!=null )
-                  {
-                  time = noise(time,mTmpVec);
-              
-                  computeOrthonormalBaseMore(time, tmpCache1);
-
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2] + baseV[4][0]*mFactor[3]);
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2] + baseV[4][1]*mFactor[3]);
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2] + baseV[4][2]*mFactor[3]);
-                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2] + baseV[4][3]*mFactor[3]);
-                  buffer[offset+4]= ((tmpCache1.a[4]*time+ tmpCache1.b[4])*time+ tmpCache1.c[4])*time+ tmpCache1.d[4] + (baseV[1][4]*mFactor[0] + baseV[2][4]*mFactor[1] + baseV[3][4]*mFactor[2] + baseV[4][4]*mFactor[3]);
-                  }
-                else
-                  {
-                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
-                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
-                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
-                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
-                  buffer[offset+4]= ((tmpCache1.a[4]*time+ tmpCache1.b[4])*time+ tmpCache1.c[4])*time+ tmpCache1.d[4];
-                  }
- 
-                break;
-                }
-      }
-    }  
-
-  }
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
diff --git a/src/main/java/org/distorted/library/type/Dynamic5D.kt b/src/main/java/org/distorted/library/type/Dynamic5D.kt
new file mode 100644
index 0000000..3e8a109
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Dynamic5D.kt
@@ -0,0 +1,563 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 5-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static5Ds.
+*/
+
+public class Dynamic5D extends Dynamic implements Data5D
+  {
+  private final Vector<Static5D> vv;
+  private Static5D prev, curr, next;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// no array bounds checking!
+  
+  private void computeVelocity(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);
+
+    tmpCache1 = 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 pv = curr.v - prev.v;
+    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 nv = next.v - curr.v;
+     
+    float d = nx*nx+ny*ny+nz*nz+nw*nw+nv*nv;
+    
+    if( d>0 )
+      {
+      float q = (float)Math.sqrt((px*px+py*py+pz*pz+pw*pw+pv*pv)/d);
+      
+      if( q>1 )
+        {
+        tmpCache1.velocity[0] = nx+px/q;
+        tmpCache1.velocity[1] = ny+py/q;
+        tmpCache1.velocity[2] = nz+pz/q;
+        tmpCache1.velocity[3] = nw+pw/q;
+        tmpCache1.velocity[4] = nv+pv/q;
+        }
+      else
+        {
+        tmpCache1.velocity[0] = px+nx*q;
+        tmpCache1.velocity[1] = py+ny*q;
+        tmpCache1.velocity[2] = pz+nz*q;
+        tmpCache1.velocity[3] = pw+nw*q;
+        tmpCache1.velocity[4] = pv+nv*q;
+        }
+      }
+    else
+      {
+      tmpCache1.velocity[0] = 0.0f;
+      tmpCache1.velocity[1] = 0.0f;
+      tmpCache1.velocity[2] = 0.0f;
+      tmpCache1.velocity[3] = 0.0f;
+      tmpCache1.velocity[4] = 0.0f;
+      }
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  private void recomputeCache()
+    {  
+    if( numPoints==1 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      curr= vv.elementAt(0);
+        
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = tmpCache1.a[4] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = tmpCache1.b[4] = 0.0f;
+      tmpCache1.c[0] = curr.x;
+      tmpCache1.c[1] = curr.y;
+      tmpCache1.c[2] = curr.z;
+      tmpCache1.c[3] = curr.w;
+      tmpCache1.c[4] = curr.v;
+      tmpCache1.d[0] = tmpCache1.d[1] = tmpCache1.d[2] = tmpCache1.d[3] = tmpCache1.d[4] = 0.0f;
+      }
+    else if( numPoints==2 )
+      {
+      tmpCache1 = vc.elementAt(0);
+      tmpCache2 = vc.elementAt(1);
+      curr= vv.elementAt(0);
+      next= vv.elementAt(1);
+      
+      tmpCache1.a[0] = tmpCache1.a[1] = tmpCache1.a[2] = tmpCache1.a[3] = tmpCache1.a[4] = 0.0f;
+      tmpCache1.b[0] = tmpCache1.b[1] = tmpCache1.b[2] = tmpCache1.b[3] = tmpCache1.b[4] = 0.0f;
+      tmpCache1.c[0] = next.x - curr.x;
+      tmpCache1.c[1] = next.y - curr.y;
+      tmpCache1.c[2] = next.z - curr.z;
+      tmpCache1.c[3] = next.w - curr.w;
+      tmpCache1.c[4] = next.v - curr.v;
+      tmpCache1.d[0] = curr.x;
+      tmpCache1.d[1] = curr.y;
+      tmpCache1.d[2] = curr.z;
+      tmpCache1.d[3] = curr.w;
+      tmpCache1.d[4] = curr.v;
+      
+      tmpCache2.a[0] = tmpCache2.a[1] = tmpCache2.a[2] = tmpCache2.a[3] = tmpCache2.a[4] = 0.0f;
+      tmpCache2.b[0] = tmpCache2.b[1] = tmpCache2.b[2] = tmpCache2.b[3] = tmpCache2.b[4] = 0.0f;
+      tmpCache2.c[0] = curr.x - next.x;
+      tmpCache2.c[1] = curr.y - next.y;
+      tmpCache2.c[2] = curr.z - next.z;
+      tmpCache2.c[3] = curr.w - next.w;
+      tmpCache2.c[4] = curr.v - next.v;
+      tmpCache2.d[0] = next.x;
+      tmpCache2.d[1] = next.y;
+      tmpCache2.d[2] = next.z;
+      tmpCache2.d[3] = next.w;
+      tmpCache2.d[4] = next.v;
+      }
+    else
+      {
+      int i, n;  
+      
+      for(i=0; i<numPoints; i++) computeVelocity(i);
+   
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        tmpCache1 = vc.elementAt(i);
+        tmpCache2 = vc.elementAt(n);
+        curr= vv.elementAt(i);
+        next= vv.elementAt(n);
+      
+        tmpCache1.cached[0] = curr.x;
+        tmpCache1.cached[1] = curr.y;
+        tmpCache1.cached[2] = curr.z;
+        tmpCache1.cached[3] = curr.w;
+        tmpCache1.cached[4] = curr.v;
+        
+        tmpCache1.a[0] = mConvexity*( 2*curr.x +   tmpCache1.velocity[0] - 2*next.x + tmpCache2.velocity[0]);
+        tmpCache1.b[0] = mConvexity*(-3*curr.x - 2* tmpCache1.velocity[0] + 3*next.x - tmpCache2.velocity[0]);
+        tmpCache1.c[0] = mConvexity*(tmpCache1.velocity[0]) + (1.0f-mConvexity)*(next.x-curr.x);
+        tmpCache1.d[0] = curr.x;
+      
+        tmpCache1.a[1] = mConvexity*( 2*curr.y +   tmpCache1.velocity[1] - 2*next.y + tmpCache2.velocity[1]);
+        tmpCache1.b[1] = mConvexity*(-3*curr.y - 2* tmpCache1.velocity[1] + 3*next.y - tmpCache2.velocity[1]);
+        tmpCache1.c[1] = mConvexity*(tmpCache1.velocity[1]) + (1.0f-mConvexity)*(next.y-curr.y);
+        tmpCache1.d[1] = curr.y;
+      
+        tmpCache1.a[2] = mConvexity*( 2*curr.z +   tmpCache1.velocity[2] - 2*next.z + tmpCache2.velocity[2]);
+        tmpCache1.b[2] = mConvexity*(-3*curr.z - 2* tmpCache1.velocity[2] + 3*next.z - tmpCache2.velocity[2]);
+        tmpCache1.c[2] = mConvexity*(tmpCache1.velocity[2]) + (1.0f-mConvexity)*(next.z-curr.z);
+        tmpCache1.d[2] = curr.z;
+        
+        tmpCache1.a[3] = mConvexity*( 2*curr.w +   tmpCache1.velocity[3] - 2*next.w + tmpCache2.velocity[3]);
+        tmpCache1.b[3] = mConvexity*(-3*curr.w - 2* tmpCache1.velocity[3] + 3*next.w - tmpCache2.velocity[3]);
+        tmpCache1.c[3] = mConvexity*(tmpCache1.velocity[3]) + (1.0f-mConvexity)*(next.w-curr.w);
+        tmpCache1.d[3] = curr.w;
+        
+        tmpCache1.a[4] = mConvexity*( 2*curr.v +   tmpCache1.velocity[4] - 2*next.v + tmpCache2.velocity[4]);
+        tmpCache1.b[4] = mConvexity*(-3*curr.v - 2* tmpCache1.velocity[4] + 3*next.v - tmpCache2.velocity[4]);
+        tmpCache1.c[4] = mConvexity*(tmpCache1.velocity[4]) + (1.0f-mConvexity)*(next.v-curr.v);
+        tmpCache1.d[4] = curr.v;
+
+        if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) smoothOutSegment(tmpCache1);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public Dynamic5D()
+    {
+    super(0,0.5f,5);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public Dynamic5D(int duration, float count)
+    {
+    super(duration,count,5);
+    vv = new Vector<>();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static5D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static5D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */  
+  public synchronized Static5D 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, float v)
+    {
+    if( location>=0 && location<numPoints )
+      {
+      curr = vv.elementAt(location);
+   
+      if( curr!=null )
+        {
+        curr.set(x,y,z,w,v);
+        cacheDirty=true;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Static5D 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 Static5D#set(float,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(Static5D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+        
+      if( vn!=null ) vn.add(new VectorNoise());
+       
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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 Static5D 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 Static5D to add.
+ */  
+  public synchronized void add(int location, Static5D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      if( vn!=null ) vn.add(new VectorNoise());
+      
+      switch(numPoints)
+        {
+        case 0: break;
+        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
+                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(Static5D 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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
+                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();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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 Dynamic increasingly deviate from this path, pseudo-randomly speeding
+ * up and slowing down, etc.
+ *
+ * @param noise The noise level. Permitted range: 0 <= noise <= 1.
+ */
+
+  public synchronized void setNoise(Static5D noise)
+    {
+    if( vn==null )
+      {
+      vn = new Vector<>();
+      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
+
+      if( mDimension>=2 )
+        {
+        mFactor = new float[mDimension-1];
+        }
+
+      mNoise = new float[mDimension];
+      }
+
+    if( noise.x<0.0f ) noise.x = 0.0f;
+    if( noise.x>1.0f ) noise.x = 1.0f;
+    if( noise.y<0.0f ) noise.y = 0.0f;
+    if( noise.y>1.0f ) noise.y = 1.0f;
+    if( noise.z<0.0f ) noise.z = 0.0f;
+    if( noise.z>1.0f ) noise.z = 1.0f;
+    if( noise.w<0.0f ) noise.w = 0.0f;
+    if( noise.w>1.0f ) noise.w = 1.0f;
+    if( noise.v<0.0f ) noise.v = 0.0f;
+    if( noise.v>1.0f ) noise.v = 1.0f;
+
+    mNoise[0] = noise.x;
+    mNoise[1] = noise.y;
+    mNoise[2] = noise.z;
+    mNoise[3] = noise.w;
+    mNoise[4] = noise.v;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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;
+              buffer[offset+4] = 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;
+              buffer[offset+4] = curr.v;
+              break;
+      case 2: curr = vv.elementAt(0);
+              next = vv.elementAt(1);
+
+              int segment= (int)(2*time);
+
+              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
+
+              if( vn!=null )
+                {
+                if( segment != mSegment )
+                  {
+                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
+                  mSegment = segment;
+                  }
+
+                time = noise(time,0);
+
+                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2] + baseV[4][0]*mFactor[3]);
+                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2] + baseV[4][1]*mFactor[3]);
+                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2] + baseV[4][2]*mFactor[3]);
+                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2] + baseV[4][3]*mFactor[3]);
+                buffer[offset+4] = (next.v-curr.v)*time + curr.v + (baseV[1][4]*mFactor[0] + baseV[2][4]*mFactor[1] + baseV[3][4]*mFactor[2] + baseV[4][4]*mFactor[3]);
+                }
+              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;
+                buffer[offset+4] = (next.v-curr.v)*time + curr.v;
+                }
+                
+              break;
+      default:computeSegmentAndTime(time);
+
+              if( mTmpVec>=0 && mTmpVec<numPoints )
+                {
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= mTmpSeg )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  int vecNext = getNext(mTmpVec,time);
+                  next = vv.elementAt(vecNext);
+                  tmpCache2 = vc.elementAt(vecNext);
+
+                  if( tmpCache2.cached[0]!=next.x || tmpCache2.cached[1]!=next.y || tmpCache2.cached[2]!=next.z || tmpCache2.cached[3]!=next.w || tmpCache2.cached[4]!=next.v ) recomputeCache();
+                  }
+
+                if( mSegment!= mTmpSeg && vn!=null ) vn.elementAt(mTmpVec).computeNoise();
+
+                mSegment = mTmpSeg;
+                time = mTmpTime-mTmpVec;
+                tmpCache1 = vc.elementAt(mTmpVec);
+                if( mSpeedMode==SPEED_MODE_SEGMENT_CONSTANT ) time = smoothSpeed(time, tmpCache1);
+
+                if( vn!=null )
+                  {
+                  time = noise(time,mTmpVec);
+              
+                  computeOrthonormalBaseMore(time, tmpCache1);
+
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2] + baseV[4][0]*mFactor[3]);
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2] + baseV[4][1]*mFactor[3]);
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2] + baseV[4][2]*mFactor[3]);
+                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2] + baseV[4][3]*mFactor[3]);
+                  buffer[offset+4]= ((tmpCache1.a[4]*time+ tmpCache1.b[4])*time+ tmpCache1.c[4])*time+ tmpCache1.d[4] + (baseV[1][4]*mFactor[0] + baseV[2][4]*mFactor[1] + baseV[3][4]*mFactor[2] + baseV[4][4]*mFactor[3]);
+                  }
+                else
+                  {
+                  buffer[offset  ]= ((tmpCache1.a[0]*time+ tmpCache1.b[0])*time+ tmpCache1.c[0])*time+ tmpCache1.d[0];
+                  buffer[offset+1]= ((tmpCache1.a[1]*time+ tmpCache1.b[1])*time+ tmpCache1.c[1])*time+ tmpCache1.d[1];
+                  buffer[offset+2]= ((tmpCache1.a[2]*time+ tmpCache1.b[2])*time+ tmpCache1.c[2])*time+ tmpCache1.d[2];
+                  buffer[offset+3]= ((tmpCache1.a[3]*time+ tmpCache1.b[3])*time+ tmpCache1.c[3])*time+ tmpCache1.d[3];
+                  buffer[offset+4]= ((tmpCache1.a[4]*time+ tmpCache1.b[4])*time+ tmpCache1.c[4])*time+ tmpCache1.d[4];
+                  }
+ 
+                break;
+                }
+      }
+    }  
+
+  }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
diff --git a/src/main/java/org/distorted/library/type/DynamicQuat.java b/src/main/java/org/distorted/library/type/DynamicQuat.java
deleted file mode 100644
index 145585c..0000000
--- a/src/main/java/org/distorted/library/type/DynamicQuat.java
+++ /dev/null
@@ -1,414 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-import java.util.Vector;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/** 
-* A 4-dimensional implementation of the Dynamic class to interpolate between a list
-* of Static4Ds.
-* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
-* SLERP. Noise not supported (yet?).
-*
-* Only unit quaternions represent valid rotations in 3D - and interpolating through rotations is the
-* most common use case for this class. No effort is done to normalize the Points though.
-*
-* Rotation Quaternion is assumed to be in the form ( axisX*sinT, axisY*sinT, axisZ*sinT, cosT ).
-*/
-
-public class DynamicQuat extends Dynamic implements Data4D
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Here we implement our own Cache as we need something slightly different.
-// omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
-//  
-// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
-// still valid and if not - rebuild the Cache.
-  
-  private class VectorCacheQuat
-    {
-    float omega, sinOmega,cosOmega;
-    float vx,vy,vz,vw;
-    }
-  
-  private final Vector<VectorCacheQuat> vc;
-  private VectorCacheQuat tmp1, tmp2;
-
-  private final Vector<Static4D> vv;
-  private Static4D 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);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void recomputeCache()
-    {  
-    if( numPoints>=2 )
-      {
-      int i, n;  
-      Static4D cu,ne;
-      VectorCacheQuat vq;
-
-      for(i=0; i<numPoints; i++)
-        {
-        n = i<numPoints-1 ? i+1:0;  
-      
-        vq= vc.elementAt(i);
-        cu= vv.elementAt(i);
-        ne= vv.elementAt(n);
-      
-        vq.vx = cu.x;
-        vq.vy = cu.y;
-        vq.vz = cu.z;
-        vq.vw = cu.w;
-    	
-        vq.cosOmega = cu.x*ne.x + cu.y*ne.y + cu.z*ne.z + cu.w*ne.w;
-        vq.sinOmega = (float)Math.sqrt(1-vq.cosOmega*vq.cosOmega);
-        vq.omega    = arcCos(vq.cosOmega);
-        }
-      }
-   
-    cacheDirty = false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default constructor.
- */
-  public DynamicQuat()
-    {
-    this(0,0.5f);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor setting the speed of interpolation and the number of revolutions.
- *
- * What constitutes 'one revolution' depends on the MODE:
- * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- *
- * @param duration number of milliseconds it takes to do one revolution.
- * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
- */
-  public DynamicQuat(int duration, float count)
-    {
-    vv         = new Vector<>();
-    vc         = new Vector<>();
-    numPoints  = 0;
-    cacheDirty = false;
-    mMode      = MODE_LOOP;
-    mDuration  = duration;
-    mCount     = count;
-    mLastPos   =-1;
-    mAccessType= ACCESS_TYPE_RANDOM;
-    mDimension = 4;
-    mSegment   =-1;
-
-    initDynamic();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the location'th Static4D.
- *   
- * @param location the index of the Point we are interested in.
- * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
- */  
-  public synchronized Static4D getPoint(int location)
-    {
-    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the location'th Point.
- * <p>
- * Rotation Quaternion is assumed to be in the form ( axisX*sinT, axisY*sinT, axisZ*sinT, cosT ).
- *
- * @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 Static4D 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 Static4D#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(Static4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(v);
-      
-      switch(numPoints)
-         {
-         case 0: 
-         case 1: vc.add(new VectorCacheQuat());
-                 vc.add(new VectorCacheQuat());
-        	     break;
-         default:vc.add(new VectorCacheQuat());
-         }
-
-       numPoints++;
-       cacheDirty = true;
-       }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new Static4D 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 Static4D to add.
- */  
-  public synchronized void add(int location, Static4D v)
-    {
-    if( v!=null )
-      {
-      vv.add(location, v);
-      
-      switch(numPoints)
-        {
-        case 0: 
-        case 1: vc.add(new VectorCacheQuat());
-                vc.add(new VectorCacheQuat());
-                break;
-        default:vc.add(location,new VectorCacheQuat());
-        }
-
-      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(Static4D 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 Dynamic, the resulting interpolated Static4D 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 Static4D to.
- * @param offset Offset in the buffer where to write the result.
- * @param time   Time of interpolation. Time=0.0 is the beginning of the first revolution, time=1.0 - the end
- *               of the first revolution, time=2.5 - the middle of the third revolution.
- *               What constitutes 'one revolution' depends on the MODE:
- *               {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
- **/
-  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;
-              int vecCurr, segment;
-              float scale0, scale1;
-
-              switch(mMode)
-                {
-                case MODE_LOOP: time = time*numPoints;
-                                segment = (int)time;
-                                vecCurr = segment;
-                                break;
-                case MODE_PATH: if( t>0.5f ) t = 1.0f-t;
-                                time = 2*t*(numPoints-1);
-                                segment = (int)(2*t*(numPoints-1));
-                                vecCurr = segment;
-                                break;
-                case MODE_JUMP: time = time*(numPoints-1);
-                                segment = (int)time;
-                                vecCurr = segment;
-                                break;
-                default       : vecCurr = 0;
-                                segment = 0;
-                }
-
-              if( vecCurr>=0 && vecCurr<numPoints )
-                {
-                int vecNext = getNext(vecCurr,t);
-
-                curr = vv.elementAt(vecCurr);
-                tmp1 = vc.elementAt(vecCurr);
-                next = vv.elementAt(vecNext);
-
-                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
-                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
-                  {
-                  tmp2 = vc.elementAt(vecNext);
-
-                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
-                  }
-
-                mSegment = segment;
-
-                time = time-vecCurr;
-
-                if( tmp1.sinOmega==0 )
-                  {
-                  scale0 = 1f;
-                  scale1 = 0f;
-                  }
-                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/DynamicQuat.kt b/src/main/java/org/distorted/library/type/DynamicQuat.kt
new file mode 100644
index 0000000..145585c
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/DynamicQuat.kt
@@ -0,0 +1,414 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+import java.util.Vector;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/** 
+* A 4-dimensional implementation of the Dynamic class to interpolate between a list
+* of Static4Ds.
+* Here, the Points are assumed to be Quaternions - thus we do the Spherical Linear Interpolation, aka
+* SLERP. Noise not supported (yet?).
+*
+* Only unit quaternions represent valid rotations in 3D - and interpolating through rotations is the
+* most common use case for this class. No effort is done to normalize the Points though.
+*
+* Rotation Quaternion is assumed to be in the form ( axisX*sinT, axisY*sinT, axisZ*sinT, cosT ).
+*/
+
+public class DynamicQuat extends Dynamic implements Data4D
+  {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Here we implement our own Cache as we need something slightly different.
+// omega, sinOmega, cosOmega - angle between pair of quaternions, its sinus and cosinus.
+//  
+// (vx,vy,vz,vw) is the original vector from vv (copied here so when interpolating we can see if it is 
+// still valid and if not - rebuild the Cache.
+  
+  private class VectorCacheQuat
+    {
+    float omega, sinOmega,cosOmega;
+    float vx,vy,vz,vw;
+    }
+  
+  private final Vector<VectorCacheQuat> vc;
+  private VectorCacheQuat tmp1, tmp2;
+
+  private final Vector<Static4D> vv;
+  private Static4D 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);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void recomputeCache()
+    {  
+    if( numPoints>=2 )
+      {
+      int i, n;  
+      Static4D cu,ne;
+      VectorCacheQuat vq;
+
+      for(i=0; i<numPoints; i++)
+        {
+        n = i<numPoints-1 ? i+1:0;  
+      
+        vq= vc.elementAt(i);
+        cu= vv.elementAt(i);
+        ne= vv.elementAt(n);
+      
+        vq.vx = cu.x;
+        vq.vy = cu.y;
+        vq.vz = cu.z;
+        vq.vw = cu.w;
+    	
+        vq.cosOmega = cu.x*ne.x + cu.y*ne.y + cu.z*ne.z + cu.w*ne.w;
+        vq.sinOmega = (float)Math.sqrt(1-vq.cosOmega*vq.cosOmega);
+        vq.omega    = arcCos(vq.cosOmega);
+        }
+      }
+   
+    cacheDirty = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default constructor.
+ */
+  public DynamicQuat()
+    {
+    this(0,0.5f);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor setting the speed of interpolation and the number of revolutions.
+ *
+ * What constitutes 'one revolution' depends on the MODE:
+ * {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ *
+ * @param duration number of milliseconds it takes to do one revolution.
+ * @param count    number of revolutions we will do. Count<=0 means 'infinite'.
+ */
+  public DynamicQuat(int duration, float count)
+    {
+    vv         = new Vector<>();
+    vc         = new Vector<>();
+    numPoints  = 0;
+    cacheDirty = false;
+    mMode      = MODE_LOOP;
+    mDuration  = duration;
+    mCount     = count;
+    mLastPos   =-1;
+    mAccessType= ACCESS_TYPE_RANDOM;
+    mDimension = 4;
+    mSegment   =-1;
+
+    initDynamic();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the location'th Static4D.
+ *   
+ * @param location the index of the Point we are interested in.
+ * @return The Static4D, if 0<=location&lt;getNumPoints(), or null otherwise.
+ */  
+  public synchronized Static4D getPoint(int location)
+    {
+    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the location'th Point.
+ * <p>
+ * Rotation Quaternion is assumed to be in the form ( axisX*sinT, axisY*sinT, axisZ*sinT, cosT ).
+ *
+ * @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 Static4D 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 Static4D#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(Static4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(v);
+      
+      switch(numPoints)
+         {
+         case 0: 
+         case 1: vc.add(new VectorCacheQuat());
+                 vc.add(new VectorCacheQuat());
+        	     break;
+         default:vc.add(new VectorCacheQuat());
+         }
+
+       numPoints++;
+       cacheDirty = true;
+       }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Adds a new Static4D 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 Static4D to add.
+ */  
+  public synchronized void add(int location, Static4D v)
+    {
+    if( v!=null )
+      {
+      vv.add(location, v);
+      
+      switch(numPoints)
+        {
+        case 0: 
+        case 1: vc.add(new VectorCacheQuat());
+                vc.add(new VectorCacheQuat());
+                break;
+        default:vc.add(location,new VectorCacheQuat());
+        }
+
+      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(Static4D 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 Dynamic, the resulting interpolated Static4D 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 Static4D to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time   Time of interpolation. Time=0.0 is the beginning of the first revolution, time=1.0 - the end
+ *               of the first revolution, time=2.5 - the middle of the third revolution.
+ *               What constitutes 'one revolution' depends on the MODE:
+ *               {@link Dynamic#MODE_LOOP}, {@link Dynamic#MODE_PATH} or {@link Dynamic#MODE_JUMP}.
+ **/
+  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;
+              int vecCurr, segment;
+              float scale0, scale1;
+
+              switch(mMode)
+                {
+                case MODE_LOOP: time = time*numPoints;
+                                segment = (int)time;
+                                vecCurr = segment;
+                                break;
+                case MODE_PATH: if( t>0.5f ) t = 1.0f-t;
+                                time = 2*t*(numPoints-1);
+                                segment = (int)(2*t*(numPoints-1));
+                                vecCurr = segment;
+                                break;
+                case MODE_JUMP: time = time*(numPoints-1);
+                                segment = (int)time;
+                                vecCurr = segment;
+                                break;
+                default       : vecCurr = 0;
+                                segment = 0;
+                }
+
+              if( vecCurr>=0 && vecCurr<numPoints )
+                {
+                int vecNext = getNext(vecCurr,t);
+
+                curr = vv.elementAt(vecCurr);
+                tmp1 = vc.elementAt(vecCurr);
+                next = vv.elementAt(vecNext);
+
+                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
+                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
+                  {
+                  tmp2 = vc.elementAt(vecNext);
+
+                  if( tmp2.vx!=next.x || tmp2.vy!=next.y || tmp2.vz!=next.z || tmp2.vw!=next.w ) recomputeCache();
+                  }
+
+                mSegment = segment;
+
+                time = time-vecCurr;
+
+                if( tmp1.sinOmega==0 )
+                  {
+                  scale0 = 1f;
+                  scale1 = 0f;
+                  }
+                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/Static.java b/src/main/java/org/distorted/library/type/Static.java
deleted file mode 100644
index 47618b9..0000000
--- a/src/main/java/org/distorted/library/type/Static.java
+++ /dev/null
@@ -1,49 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Any-dimensional data structure containing arbitrary number of floats. The floats have no
- * particular meaning; when this data structure is used in Dynamics, we can think of it as a
- * N-dimensional Point a few of which the Dynamic interpolates between.
- */
-
-public abstract class Static
-  {
-  private final int mDimension;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Static(int dim)
-    {
-    mDimension = dim;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the Dimension, ie number of floats in this Static.
- */
-  public int getDimension()
-    {
-    return mDimension;
-    }
-  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/type/Static.kt b/src/main/java/org/distorted/library/type/Static.kt
new file mode 100644
index 0000000..47618b9
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static.kt
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Any-dimensional data structure containing arbitrary number of floats. The floats have no
+ * particular meaning; when this data structure is used in Dynamics, we can think of it as a
+ * N-dimensional Point a few of which the Dynamic interpolates between.
+ */
+
+public abstract class Static
+  {
+  private final int mDimension;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static(int dim)
+    {
+    mDimension = dim;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the Dimension, ie number of floats in this Static.
+ */
+  public int getDimension()
+    {
+    return mDimension;
+    }
+  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/type/Static1D.java b/src/main/java/org/distorted/library/type/Static1D.java
deleted file mode 100644
index 0da4380..0000000
--- a/src/main/java/org/distorted/library/type/Static1D.java
+++ /dev/null
@@ -1,113 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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 Dynamics, we can think of it as a 1-dimensional Point
- * a few of which the Dynamic interpolates between.
- */
-
-public class Static1D extends Static implements Data1D
-  {
-  float x;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the single float to ox.   
- *   
- * @param ox value of the single float.
- */  
-  public Static1D(float ox)
-    {
-    super(1);
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public Static1D(Static1D sta)
-    {
-    super(1);
-    x = sta.x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the single float.
- * 
- * @param ox new value of the single float.
- */
-  public void set(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy a Static1D.
- */
-  public void set(Static1D s)
-    {
-    x = s.x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the first float.
- *
- * @param ox new value of the first float.
- */
-  public void set0(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the float contained.
- * 
- * @return The single float.
- */
-  public float get0()
-    {
-    return x;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time not used
- * @param step not used
- * @return <code>false</code>
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    buffer[offset] = x;
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Static1D.kt b/src/main/java/org/distorted/library/type/Static1D.kt
new file mode 100644
index 0000000..0da4380
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static1D.kt
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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 Dynamics, we can think of it as a 1-dimensional Point
+ * a few of which the Dynamic interpolates between.
+ */
+
+public class Static1D extends Static implements Data1D
+  {
+  float x;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the single float to ox.   
+ *   
+ * @param ox value of the single float.
+ */  
+  public Static1D(float ox)
+    {
+    super(1);
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+  public Static1D(Static1D sta)
+    {
+    super(1);
+    x = sta.x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the single float.
+ * 
+ * @param ox new value of the single float.
+ */
+  public void set(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy a Static1D.
+ */
+  public void set(Static1D s)
+    {
+    x = s.x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the first float.
+ *
+ * @param ox new value of the first float.
+ */
+  public void set0(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the float contained.
+ * 
+ * @return The single float.
+ */
+  public float get0()
+    {
+    return x;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time not used
+ * @param step not used
+ * @return <code>false</code>
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    buffer[offset] = x;
+    return false;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/type/Static2D.java b/src/main/java/org/distorted/library/type/Static2D.java
deleted file mode 100644
index 2d827ac..0000000
--- a/src/main/java/org/distorted/library/type/Static2D.java
+++ /dev/null
@@ -1,142 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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 Dynamics, we can think of it as a 2-dimensional Point
- * a few of which the Dynamic interpolates between.
- */
-
-public class Static2D extends Static implements Data2D
-  {
-  float x,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 Static2D(float ox, float oy)
-    {
-    super(2);
-    x = ox;
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public Static2D(Static2D sta)
-    {
-    super(2);
-    x = sta.x;
-    y = sta.y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy a Static2D.
- */
-  public void set(Static2D s)
-    {
-    x = s.x;
-    y = s.y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the first float.
- *
- * @param ox new value of the first float.
- */
-  public void set0(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the second float.
- *
- * @param oy new value of the second float.
- */
-  public void set1(float oy)
-    {
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the first float contained.
- *
- * @return The first float.
- */
-  public float get0()
-    {
-    return x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the second float contained.
- * 
- * @return The second float.
- */
-  public float get1()
-    {
-    return y;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time not used
- * @param step not used
- * @return <code>false</code>
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    buffer[offset  ] = x;
-    buffer[offset+1] = y;
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Static2D.kt b/src/main/java/org/distorted/library/type/Static2D.kt
new file mode 100644
index 0000000..2d827ac
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static2D.kt
@@ -0,0 +1,142 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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 Dynamics, we can think of it as a 2-dimensional Point
+ * a few of which the Dynamic interpolates between.
+ */
+
+public class Static2D extends Static implements Data2D
+  {
+  float x,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 Static2D(float ox, float oy)
+    {
+    super(2);
+    x = ox;
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+  public Static2D(Static2D sta)
+    {
+    super(2);
+    x = sta.x;
+    y = sta.y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy a Static2D.
+ */
+  public void set(Static2D s)
+    {
+    x = s.x;
+    y = s.y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the first float.
+ *
+ * @param ox new value of the first float.
+ */
+  public void set0(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the second float.
+ *
+ * @param oy new value of the second float.
+ */
+  public void set1(float oy)
+    {
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the first float contained.
+ *
+ * @return The first float.
+ */
+  public float get0()
+    {
+    return x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the second float contained.
+ * 
+ * @return The second float.
+ */
+  public float get1()
+    {
+    return y;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time not used
+ * @param step not used
+ * @return <code>false</code>
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    buffer[offset  ] = x;
+    buffer[offset+1] = y;
+    return false;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/type/Static3D.java b/src/main/java/org/distorted/library/type/Static3D.java
deleted file mode 100644
index c7dc291..0000000
--- a/src/main/java/org/distorted/library/type/Static3D.java
+++ /dev/null
@@ -1,171 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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 Dynamics, we can think of it as a 3-dimensional Point
- * a few of which the Dynamic interpolates between.
- */
-
-public class Static3D extends Static implements Data3D
-  {
-  float x,y,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 Static3D(float vx, float vy, float vz)
-    {
-    super(3);
-    x = vx;
-    y = vy;
-    z = vz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public Static3D(Static3D sta)
-    {
-    super(3);
-    x = sta.x;
-    y = sta.y;
-    z = sta.z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy a Static3D.
- */
-  public void set(Static3D s)
-    {
-    x = s.x;
-    y = s.y;
-    z = s.z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the first float.
- *
- * @param ox new value of the first float.
- */
-  public void set0(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the second float.
- *
- * @param oy new value of the second float.
- */
-  public void set1(float oy)
-    {
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the third float.
- *
- * @param oz new value of the third float.
- */
-  public void set2(float oz)
-    {
-    z = oz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the first float contained.
- *
- * @return The first float.
- */
-  public float get0()
-    {
-    return x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the second float contained.
- *
- * @return The second float.
- */
-  public float get1()
-    {
-    return y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the third float contained.
- * 
- * @return The third float.
- */
-  public float get2()
-    {
-    return z;  
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time not used
- * @param step not used
- * @return <code>false</code>
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    buffer[offset  ] = x;
-    buffer[offset+1] = y;
-    buffer[offset+2] = z;
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Static3D.kt b/src/main/java/org/distorted/library/type/Static3D.kt
new file mode 100644
index 0000000..c7dc291
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static3D.kt
@@ -0,0 +1,171 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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 Dynamics, we can think of it as a 3-dimensional Point
+ * a few of which the Dynamic interpolates between.
+ */
+
+public class Static3D extends Static implements Data3D
+  {
+  float x,y,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 Static3D(float vx, float vy, float vz)
+    {
+    super(3);
+    x = vx;
+    y = vy;
+    z = vz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+  public Static3D(Static3D sta)
+    {
+    super(3);
+    x = sta.x;
+    y = sta.y;
+    z = sta.z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy a Static3D.
+ */
+  public void set(Static3D s)
+    {
+    x = s.x;
+    y = s.y;
+    z = s.z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the first float.
+ *
+ * @param ox new value of the first float.
+ */
+  public void set0(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the second float.
+ *
+ * @param oy new value of the second float.
+ */
+  public void set1(float oy)
+    {
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the third float.
+ *
+ * @param oz new value of the third float.
+ */
+  public void set2(float oz)
+    {
+    z = oz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the first float contained.
+ *
+ * @return The first float.
+ */
+  public float get0()
+    {
+    return x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the second float contained.
+ *
+ * @return The second float.
+ */
+  public float get1()
+    {
+    return y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the third float contained.
+ * 
+ * @return The third float.
+ */
+  public float get2()
+    {
+    return z;  
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time not used
+ * @param step not used
+ * @return <code>false</code>
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    buffer[offset  ] = x;
+    buffer[offset+1] = y;
+    buffer[offset+2] = z;
+    return false;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/type/Static4D.java b/src/main/java/org/distorted/library/type/Static4D.java
deleted file mode 100644
index 3ffbedb..0000000
--- a/src/main/java/org/distorted/library/type/Static4D.java
+++ /dev/null
@@ -1,200 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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 Dynamics, we can think of it as a 4-dimensional Point
- * a few of which the Dynamic interpolates between.
- */
-
-public class Static4D extends Static implements Data4D
-  {
-  float x,y,z,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 Static4D(float vx, float vy, float vz, float vw)
-    {
-    super(4);
-    x = vx;
-    y = vy;
-    z = vz;
-    w = vw;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public Static4D(Static4D sta)
-    {
-    super(4);
-    x = sta.x;
-    y = sta.y;
-    z = sta.z;
-    w = sta.w;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy a Static4D.
- */
-  public void set(Static4D s)
-    {
-    x = s.x;
-    y = s.y;
-    z = s.z;
-    w = s.w;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the first float.
- *
- * @param ox new value of the first float.
- */
-  public void set0(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the second float.
- *
- * @param oy new value of the second float.
- */
-  public void set1(float oy)
-    {
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the third float.
- *
- * @param oz new value of the third float.
- */
-  public void set2(float oz)
-    {
-    z = oz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the fourth float.
- *
- * @param ow new value of the fourth float.
- */
-  public void set3(float ow)
-    {
-    w = ow;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the first float contained.
- *
- * @return The first float.
- */
-  public float get0()
-    {
-    return x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the second float contained.
- *
- * @return The second float.
- */
-  public float get1()
-    {
-    return y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the third float contained.
- *
- * @return The third float.
- */
-  public float get2()
-    {
-    return z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the fourth float contained.
- *
- * @return The fourth float.
- */
-  public float get3()
-    {
-    return w;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time not used
- * @param step not used
- * @return <code>false</code>
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    buffer[offset  ] = x;
-    buffer[offset+1] = y;
-    buffer[offset+2] = z;
-    buffer[offset+3] = w;
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Static4D.kt b/src/main/java/org/distorted/library/type/Static4D.kt
new file mode 100644
index 0000000..3ffbedb
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static4D.kt
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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 Dynamics, we can think of it as a 4-dimensional Point
+ * a few of which the Dynamic interpolates between.
+ */
+
+public class Static4D extends Static implements Data4D
+  {
+  float x,y,z,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 Static4D(float vx, float vy, float vz, float vw)
+    {
+    super(4);
+    x = vx;
+    y = vy;
+    z = vz;
+    w = vw;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+  public Static4D(Static4D sta)
+    {
+    super(4);
+    x = sta.x;
+    y = sta.y;
+    z = sta.z;
+    w = sta.w;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 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;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy a Static4D.
+ */
+  public void set(Static4D s)
+    {
+    x = s.x;
+    y = s.y;
+    z = s.z;
+    w = s.w;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the first float.
+ *
+ * @param ox new value of the first float.
+ */
+  public void set0(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the second float.
+ *
+ * @param oy new value of the second float.
+ */
+  public void set1(float oy)
+    {
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the third float.
+ *
+ * @param oz new value of the third float.
+ */
+  public void set2(float oz)
+    {
+    z = oz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the fourth float.
+ *
+ * @param ow new value of the fourth float.
+ */
+  public void set3(float ow)
+    {
+    w = ow;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the first float contained.
+ *
+ * @return The first float.
+ */
+  public float get0()
+    {
+    return x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the second float contained.
+ *
+ * @return The second float.
+ */
+  public float get1()
+    {
+    return y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the third float contained.
+ *
+ * @return The third float.
+ */
+  public float get2()
+    {
+    return z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the fourth float contained.
+ *
+ * @return The fourth float.
+ */
+  public float get3()
+    {
+    return w;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time not used
+ * @param step not used
+ * @return <code>false</code>
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    buffer[offset  ] = x;
+    buffer[offset+1] = y;
+    buffer[offset+2] = z;
+    buffer[offset+3] = w;
+    return false;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/type/Static5D.java b/src/main/java/org/distorted/library/type/Static5D.java
deleted file mode 100644
index 8d6d247..0000000
--- a/src/main/java/org/distorted/library/type/Static5D.java
+++ /dev/null
@@ -1,229 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// This library is free software; you can redistribute it and/or                                 //
-// modify it under the terms of the GNU Lesser General Public                                    //
-// License as published by the Free Software Foundation; either                                  //
-// version 2.1 of the License, or (at your option) any later version.                            //
-//                                                                                               //
-// This library 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                             //
-// Lesser General Public License for more details.                                               //
-//                                                                                               //
-// You should have received a copy of the GNU Lesser General Public                              //
-// License along with this library; if not, write to the Free Software                           //
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.type;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * A 5-dimensional data structure containing five floats. The floats have no particular meaning; 
- * when this data structure is used in Dynamics, we can think of it as a 5-dimensional Point
- * a few of which the Dynamic interpolates between.
- */
-
-public class Static5D extends Static implements Data5D
-  {
-  float x,y,z,w,v;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor that initialises the value of the five floats to (vx,vy,vz,vw,vv).   
- *   
- * @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.
- * @param vv value of the fifth float.
- */ 
-  public Static5D(float vx, float vy, float vz, float vw, float vv)
-    {
-    super(5);
-    x = vx;
-    y = vy;
-    z = vz;
-    w = vw;
-    v = vv;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public Static5D(Static5D sta)
-    {
-    super(5);
-    x = sta.x;
-    y = sta.y;
-    z = sta.z;
-    w = sta.w;
-    v = sta.v;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Reset the value of the floats to (vx,vy,vz,vw,vv).
- * 
- * @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
- * @param vv new value of the fifth float
- */
-  public void set(float vx, float vy, float vz, float vw, float vv)
-    {
-    x = vx;
-    y = vy;
-    z = vz;
-    w = vw;
-    v = vv;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy a Static5D.
- */
-  public void set(Static5D s)
-    {
-    x = s.x;
-    y = s.y;
-    z = s.z;
-    w = s.w;
-    v = s.v;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the first float.
- *
- * @param ox new value of the first float.
- */
-  public void set0(float ox)
-    {
-    x = ox;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the second float.
- *
- * @param oy new value of the second float.
- */
-  public void set1(float oy)
-    {
-    y = oy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the third float.
- *
- * @param oz new value of the third float.
- */
-  public void set2(float oz)
-    {
-    z = oz;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the fourth float.
- *
- * @param ow new value of the fourth float.
- */
-  public void set3(float ow)
-    {
-    w = ow;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resets the value of the fifth float.
- *
- * @param ov new value of the fifth float.
- */
-  public void set4(float ov)
-    {
-    v = ov;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the first float contained.
- *
- * @return The first float.
- */
-  public float get0()
-    {
-    return x;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the second float contained.
- *
- * @return The second float.
- */
-  public float get1()
-    {
-    return y;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the third float contained.
- *
- * @return The third float.
- */
-  public float get2()
-    {
-    return z;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the fourth float contained.
- *
- * @return The fourth float.
- */
-  public float get3()
-    {
-    return w;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the value of the fifth float contained.
- * 
- * @return The fifth float.
- */
-  public float get4()
-    {
-    return v;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
- *
- * @param buffer Float buffer we will write the results to.
- * @param offset Offset in the buffer where to write the result.
- * @param time not used
- * @param step not used
- * @return <code>false</code>
- */
-  public boolean get(float[] buffer, int offset, long time, long step)
-    {
-    buffer[offset  ] = x;
-    buffer[offset+1] = y;
-    buffer[offset+2] = z;
-    buffer[offset+3] = w;
-    buffer[offset+4] = v;
-    return false;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/type/Static5D.kt b/src/main/java/org/distorted/library/type/Static5D.kt
new file mode 100644
index 0000000..8d6d247
--- /dev/null
+++ b/src/main/java/org/distorted/library/type/Static5D.kt
@@ -0,0 +1,229 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.type;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A 5-dimensional data structure containing five floats. The floats have no particular meaning; 
+ * when this data structure is used in Dynamics, we can think of it as a 5-dimensional Point
+ * a few of which the Dynamic interpolates between.
+ */
+
+public class Static5D extends Static implements Data5D
+  {
+  float x,y,z,w,v;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor that initialises the value of the five floats to (vx,vy,vz,vw,vv).   
+ *   
+ * @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.
+ * @param vv value of the fifth float.
+ */ 
+  public Static5D(float vx, float vy, float vz, float vw, float vv)
+    {
+    super(5);
+    x = vx;
+    y = vy;
+    z = vz;
+    w = vw;
+    v = vv;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+  public Static5D(Static5D sta)
+    {
+    super(5);
+    x = sta.x;
+    y = sta.y;
+    z = sta.z;
+    w = sta.w;
+    v = sta.v;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Reset the value of the floats to (vx,vy,vz,vw,vv).
+ * 
+ * @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
+ * @param vv new value of the fifth float
+ */
+  public void set(float vx, float vy, float vz, float vw, float vv)
+    {
+    x = vx;
+    y = vy;
+    z = vz;
+    w = vw;
+    v = vv;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy a Static5D.
+ */
+  public void set(Static5D s)
+    {
+    x = s.x;
+    y = s.y;
+    z = s.z;
+    w = s.w;
+    v = s.v;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the first float.
+ *
+ * @param ox new value of the first float.
+ */
+  public void set0(float ox)
+    {
+    x = ox;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the second float.
+ *
+ * @param oy new value of the second float.
+ */
+  public void set1(float oy)
+    {
+    y = oy;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the third float.
+ *
+ * @param oz new value of the third float.
+ */
+  public void set2(float oz)
+    {
+    z = oz;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the fourth float.
+ *
+ * @param ow new value of the fourth float.
+ */
+  public void set3(float ow)
+    {
+    w = ow;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resets the value of the fifth float.
+ *
+ * @param ov new value of the fifth float.
+ */
+  public void set4(float ov)
+    {
+    v = ov;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the first float contained.
+ *
+ * @return The first float.
+ */
+  public float get0()
+    {
+    return x;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the second float contained.
+ *
+ * @return The second float.
+ */
+  public float get1()
+    {
+    return y;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the third float contained.
+ *
+ * @return The third float.
+ */
+  public float get2()
+    {
+    return z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the fourth float contained.
+ *
+ * @return The fourth float.
+ */
+  public float get3()
+    {
+    return w;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return the value of the fifth float contained.
+ * 
+ * @return The fifth float.
+ */
+  public float get4()
+    {
+    return v;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 'Interpolation' between the single Point (i.e. always this very value) returned to the buffer.
+ *
+ * @param buffer Float buffer we will write the results to.
+ * @param offset Offset in the buffer where to write the result.
+ * @param time not used
+ * @param step not used
+ * @return <code>false</code>
+ */
+  public boolean get(float[] buffer, int offset, long time, long step)
+    {
+    buffer[offset  ] = x;
+    buffer[offset+1] = y;
+    buffer[offset+2] = z;
+    buffer[offset+3] = w;
+    buffer[offset+4] = v;
+    return false;
+    }
+  }
