commit 1e256a15e7035bae0488c0cc3328c6e06f26ac11
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon May 17 00:58:56 2021 +0200

    RubikControl: progress.

diff --git a/src/main/java/org/distorted/control/RubikControlWhole.java b/src/main/java/org/distorted/control/RubikControlWhole.java
index 4d2699a3..3f49ec5b 100644
--- a/src/main/java/org/distorted/control/RubikControlWhole.java
+++ b/src/main/java/org/distorted/control/RubikControlWhole.java
@@ -29,7 +29,6 @@ import org.distorted.library.main.DistortedNode;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshQuad;
-import org.distorted.library.message.EffectListener;
 import org.distorted.library.type.Dynamic;
 import org.distorted.library.type.Dynamic3D;
 import org.distorted.library.type.Static3D;
@@ -41,7 +40,7 @@ import java.io.InputStream;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class RubikControlWhole implements EffectListener
+class RubikControlWhole
   {
   private static final int NUM_NODE = 4;
   private static final int NUM_EFFE = 4;
@@ -60,7 +59,7 @@ class RubikControlWhole implements EffectListener
   private DistortedEffects[] mEffects;
   private DistortedNode[] mNodes;
   private long mEffectID;
-  private int mCurrentStage, mWidth, mHeight;
+  private int mCurrentStage;
 
   private MeshQuad mQuad;
   private DistortedTexture mTextureShad, mTextureCirc;
@@ -75,9 +74,9 @@ class RubikControlWhole implements EffectListener
   private MatrixEffectMove mMoveHand2, mMoveShad2;
   private MatrixEffectScale mScaleHand2, mScaleShad2;
 
-  private Dynamic3D mDyn1, mDyn2;
-  private Static3D mPosition1, mPosition2;
-  private float[] mHandPosition;
+  private Dynamic3D mDyn1, mDyn2, mDyn3, mDyn4;
+  private Static3D mPosition1, mPosition2, mPosition3, mPosition4;
+  private float[] tmpBuffer;
   private long mLastTime, mDiffTime;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -163,6 +162,20 @@ class RubikControlWhole implements EffectListener
     mDyn2.resetToBeginning();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void resetDynamics4(int stage)
+    {
+    int dur = DUR[stage-1];
+
+    mDyn3.removeAll();
+    mDyn4.removeAll();
+    mDyn3.setDuration(dur);
+    mDyn4.setDuration(dur);
+    mDyn3.resetToBeginning();
+    mDyn4.resetToBeginning();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // first finger appears and approaches the screen
 
@@ -170,6 +183,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(1);
     resetDynamics2(1);
+    resetDynamics3(1);
 
     Static3D point0h = new Static3D(-X0    ,-Y1    , 0);
     Static3D point1h = new Static3D(-X1    ,-Y1    , 0);
@@ -182,20 +196,27 @@ class RubikControlWhole implements EffectListener
 
     Static3D pointSc = new Static3D(s033,s033,s033);
 
-    mDynMoveHand1.add(point0h);
-    mDynMoveHand1.add(point1h);
-    mDynMoveHand1.add(point2h);
-    mDynMoveHand1.add(point2h);
-    mDynMoveHand1.add(point3h);
-    mDynMoveShad1.add(point0s);
-    mDynMoveShad1.add(point1s);
-    mDynMoveShad1.add(point2s);
-    mDynMoveShad1.add(point2s);
-    mDynMoveShad1.add(point3s);
+    mDyn1.add(point0h);
+    mDyn1.add(point1h);
+    mDyn1.add(point2h);
+    mDyn1.add(point2h);
+    mDyn1.add(point3h);
+    mDyn2.add(point0s);
+    mDyn2.add(point1s);
+    mDyn2.add(point2s);
+    mDyn2.add(point2s);
+    mDyn2.add(point3s);
+
+    mPosition1.set(point0h);
+    mPosition2.set(point0s);
+
+    mDynMoveHand1.add(mPosition1);
+    mDynMoveShad1.add(mPosition2);
+
     mDynScaleHand1.add(pointSc);
     mDynScaleShad1.add(pointSc);
 
-    mMoveHand1.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -204,21 +225,24 @@ class RubikControlWhole implements EffectListener
   private void setEffectsStage2()
     {
     resetDynamics1(2);
+    resetDynamics3(2);
 
     Static3D point3h = new Static3D(-X2  +D,-Y1  +D, 0);
     Static3D scaleS  = new Static3D(s001,s001,s001);
     Static3D scaleF  = new Static3D(s014,s014,s014);
     Static3D pointH  = new Static3D(s033,s033,s033);
 
+    mPosition1.set(scaleS);
+    mDyn1.add(scaleS);
+    mDyn1.add(scaleF);
+
     mDynMoveHand1.add(point3h);
     mDynMoveShad1.add(point3h);
     mDynScaleHand1.add(pointH);
-    mDynScaleShad1.add(scaleS);
-    mDynScaleShad1.add(scaleF);
-
-    mScaleShad1.notifyWhenFinished(this);
+    mDynScaleShad1.add(mPosition1);
 
     mNodes[0].changeInputSurface(mTextureCirc);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -267,19 +291,23 @@ class RubikControlWhole implements EffectListener
   private void setEffectsStage4()
     {
     resetDynamics1(4);
+    resetDynamics3(4);
 
     Static3D point3h = new Static3D(-X2+D,-Y1+D, 0);
     Static3D scaleS  = new Static3D(s014,s014,s014);
     Static3D scaleF  = new Static3D(s001,s001,s001);
     Static3D pointH  = new Static3D(s033,s033,s033);
 
+    mDyn1.add(scaleS);
+    mDyn1.add(scaleF);
+    mPosition1.set(scaleS);
+
     mDynMoveHand1.add(point3h);
     mDynMoveShad1.add(point3h);
     mDynScaleHand1.add(pointH);
-    mDynScaleShad1.add(scaleS);
-    mDynScaleShad1.add(scaleF);
+    mDynScaleShad1.add(mPosition1);
 
-    mScaleShad1.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -288,21 +316,28 @@ class RubikControlWhole implements EffectListener
   private void setEffectsStage5()
     {
     resetDynamics1(5);
+    resetDynamics3(5);
 
     Static3D pointH = new Static3D(-X2    ,-Y1    , 0);
     Static3D point0 = new Static3D(-X2  +D,-Y1  +D, 0);
     Static3D pointS = new Static3D(-X2+2*D,-Y1+2*D, 0);
     Static3D pointSc = new Static3D(s033,s033,s033);
 
+    mPosition1.set(point0);
+    mPosition2.set(point0);
+
+    mDyn1.add(point0);
+    mDyn1.add(pointH);
+    mDyn2.add(point0);
+    mDyn2.add(pointS);
+
     mDynScaleHand1.add(pointSc);
     mDynScaleShad1.add(pointSc);
-    mDynMoveHand1.add(point0);
-    mDynMoveHand1.add(pointH);
-    mDynMoveShad1.add(point0);
-    mDynMoveShad1.add(pointS);
+    mDynMoveHand1.add(mPosition1);
+    mDynMoveShad1.add(mPosition2);
 
-    mMoveShad1.notifyWhenFinished(this);
     mNodes[0].changeInputSurface(mTextureShad);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -312,6 +347,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(6);
     resetDynamics2(6);
+    resetDynamics3(6);
 
     Static3D pointH = new Static3D(-X2    ,-Y1    , 0);
     Static3D pointS = new Static3D(-X2+2*D,-Y1+2*D, 0);
@@ -328,20 +364,26 @@ class RubikControlWhole implements EffectListener
     Static3D point0s = new Static3D( X0+2*D, Y1+2*D, 0);
     Static3D point1s = new Static3D( X1+2*D, Y1+2*D, 0);
     Static3D point2s = new Static3D( X2+2*D, Y1+2*D, 0);
-    Static3D pointSm= new Static3D(-s033,s033,s033);
+    Static3D pointSm = new Static3D(-s033,s033,s033);
+
+    mPosition1.set(point0h);
+    mPosition2.set(point0s);
+
+    mDyn1.add(point0h);
+    mDyn1.add(point1h);
+    mDyn1.add(point2h);
+    mDyn1.add(point2h);
+    mDyn2.add(point0s);
+    mDyn2.add(point1s);
+    mDyn2.add(point2s);
+    mDyn2.add(point2s);
 
-    mDynMoveHand2.add(point0h);
-    mDynMoveHand2.add(point1h);
-    mDynMoveHand2.add(point2h);
-    mDynMoveHand2.add(point2h);
-    mDynMoveShad2.add(point0s);
-    mDynMoveShad2.add(point1s);
-    mDynMoveShad2.add(point2s);
-    mDynMoveShad2.add(point2s);
     mDynScaleHand2.add(pointSm);
     mDynScaleShad2.add(pointSm);
+    mDynMoveHand2.add(mPosition1);
+    mDynMoveShad2.add(mPosition2);
 
-    mMoveHand2.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -351,32 +393,45 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(7);
     resetDynamics2(7);
+    resetDynamics3(7);
+    resetDynamics4(7);
+
+    Static3D point1Sc= new Static3D( s033,s033,s033);
+    Static3D point2Sc= new Static3D(-s033,s033,s033);
+    mDynScaleHand1.add(point1Sc);
+    mDynScaleShad1.add(point1Sc);
+    mDynScaleHand2.add(point2Sc);
+    mDynScaleShad2.add(point2Sc);
 
     Static3D point1H = new Static3D(-X2    ,-Y1    , 0);
     Static3D point1F = new Static3D(-X2  +D,-Y1  +D, 0);
     Static3D point1S = new Static3D(-X2+2*D,-Y1+2*D, 0);
-    Static3D point1Sc= new Static3D(s033,s033,s033);
 
-    mDynScaleHand1.add(point1Sc);
-    mDynScaleShad1.add(point1Sc);
-    mDynMoveHand1.add(point1H);
-    mDynMoveHand1.add(point1F);
-    mDynMoveShad1.add(point1S);
-    mDynMoveShad1.add(point1F);
+    mDyn1.add(point1H);
+    mDyn1.add(point1F);
+    mDyn2.add(point1S);
+    mDyn2.add(point1F);
+
+    mPosition1.set(point1H);
+    mPosition2.set(point1S);
+    mDynMoveHand1.add(mPosition1);
+    mDynMoveShad1.add(mPosition2);
 
     Static3D point2H = new Static3D( X2    , Y1    , 0);
     Static3D point2F = new Static3D( X2  +D, Y1  +D, 0);
     Static3D point2S = new Static3D( X2+2*D, Y1+2*D, 0);
-    Static3D point2Sc= new Static3D(-s033,s033,s033);
 
-    mDynScaleHand2.add(point2Sc);
-    mDynScaleShad2.add(point2Sc);
-    mDynMoveHand2.add(point2H);
-    mDynMoveHand2.add(point2F);
-    mDynMoveShad2.add(point2S);
-    mDynMoveShad2.add(point2F);
+    mDyn3.add(point2H);
+    mDyn3.add(point2F);
+    mDyn4.add(point2S);
+    mDyn4.add(point2F);
 
-    mMoveHand2.notifyWhenFinished(this);
+    mPosition3.set(point2H);
+    mPosition4.set(point2S);
+    mDynMoveHand2.add(mPosition3);
+    mDynMoveShad2.add(mPosition4);
+
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -386,6 +441,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(8);
     resetDynamics2(8);
+    resetDynamics3(8);
 
     Static3D point1h= new Static3D(-X2  +D,-Y1  +D, 0);
     Static3D point2h= new Static3D( X2  +D, Y1  +D, 0);
@@ -396,22 +452,28 @@ class RubikControlWhole implements EffectListener
     Static3D scale2F = new Static3D(-s014,s014,s014);
     Static3D point2H = new Static3D(-s033,s033,s033);
 
+    mPosition1.set(scale1S);
+    mDyn1.add(scale1S);
+    mDyn1.add(scale1F);
+
     mDynMoveHand1.add(point1h);
     mDynMoveShad1.add(point1h);
     mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(scale1S);
-    mDynScaleShad1.add(scale1F);
+    mDynScaleShad1.add(mPosition1);
+
+    mPosition2.set(scale2S);
+    mDyn2.add(scale2S);
+    mDyn2.add(scale2F);
 
     mDynMoveHand2.add(point2h);
     mDynMoveShad2.add(point2h);
     mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(scale2S);
-    mDynScaleShad2.add(scale2F);
-
-    mScaleShad2.notifyWhenFinished(this);
+    mDynScaleShad2.add(mPosition2);
 
     mNodes[0].changeInputSurface(mTextureCirc);
     mNodes[1].changeInputSurface(mTextureCirc);
+
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -460,6 +522,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(10);
     resetDynamics2(10);
+    resetDynamics3(10);
 
     Static3D scale1F = new Static3D( s014,s014,s014);
     Static3D point1H = new Static3D( s033,s033,s033);
@@ -471,38 +534,34 @@ class RubikControlWhole implements EffectListener
     Static3D point2= new Static3D(( X2+D)*F,( Y1+D)*F, 0);
     Static3D point3= new Static3D(( X2+D)*F,(-Y1+D)*F, 0);
 
-    mDynMoveHand1.add(point0);
-    mDynMoveHand1.add(point1);
-    mDynMoveHand1.add(point2);
-    mDynMoveHand1.add(point3);
-    mDynMoveHand1.add(point0);
-    mDynMoveShad1.add(point0);
-    mDynMoveShad1.add(point1);
-    mDynMoveShad1.add(point2);
-    mDynMoveShad1.add(point3);
-    mDynMoveShad1.add(point0);
     mDynScaleHand1.add(point1H);
     mDynScaleShad1.add(scale1F);
-
-    mDynMoveHand2.add(point2);
-    mDynMoveHand2.add(point3);
-    mDynMoveHand2.add(point0);
-    mDynMoveHand2.add(point1);
-    mDynMoveHand2.add(point2);
-    mDynMoveShad2.add(point2);
-    mDynMoveShad2.add(point3);
-    mDynMoveShad2.add(point0);
-    mDynMoveShad2.add(point1);
-    mDynMoveShad2.add(point2);
     mDynScaleHand2.add(point2H);
     mDynScaleShad2.add(scale2F);
 
-    mDynMoveHand1.setConvexity(1.0f);
-    mDynMoveHand2.setConvexity(1.0f);
-    mDynMoveShad1.setConvexity(1.0f);
-    mDynMoveShad2.setConvexity(1.0f);
+    mDyn1.add(point0);
+    mDyn1.add(point1);
+    mDyn1.add(point2);
+    mDyn1.add(point3);
+    mDyn1.add(point0);
+    mDyn2.add(point2);
+    mDyn2.add(point3);
+    mDyn2.add(point0);
+    mDyn2.add(point1);
+    mDyn2.add(point2);
+
+    mDyn1.setConvexity(1.0f);
+    mDyn2.setConvexity(1.0f);
+
+    mPosition1.set(point0);
+    mPosition2.set(point2);
+
+    mDynMoveHand1.add(mPosition1);
+    mDynMoveShad1.add(mPosition1);
+    mDynMoveHand2.add(mPosition2);
+    mDynMoveShad2.add(mPosition2);
 
-    mMoveShad1.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -512,6 +571,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(11);
     resetDynamics2(11);
+    resetDynamics3(11);
 
     Static3D point1s= new Static3D( -X2+D   , -Y1+D   , 0);
     Static3D point2s= new Static3D(  X2+D   ,  Y1+D   , 0);
@@ -522,26 +582,28 @@ class RubikControlWhole implements EffectListener
     Static3D scale2F= new Static3D(-s014,s014,s014);
     Static3D point2H= new Static3D(-s033,s033,s033);
 
-    mDynMoveHand1.add(point1f);
-    mDynMoveHand1.add(point1s);
-    mDynMoveShad1.add(point1f);
-    mDynMoveShad1.add(point1s);
     mDynScaleHand1.add(point1H);
     mDynScaleShad1.add(scale1F);
-
-    mDynMoveHand2.add(point2f);
-    mDynMoveHand2.add(point2s);
-    mDynMoveShad2.add(point2f);
-    mDynMoveShad2.add(point2s);
     mDynScaleHand2.add(point2H);
     mDynScaleShad2.add(scale2F);
 
-    mDynMoveHand1.setConvexity(0.0f);
-    mDynMoveHand2.setConvexity(0.0f);
-    mDynMoveShad1.setConvexity(0.0f);
-    mDynMoveShad2.setConvexity(0.0f);
+    mDyn1.add(point1f);
+    mDyn1.add(point1s);
+    mDyn2.add(point2f);
+    mDyn2.add(point2s);
+
+    mDyn1.setConvexity(0.0f);
+    mDyn2.setConvexity(0.0f);
+
+    mPosition1.set(point1f);
+    mPosition2.set(point2f);
+
+    mDynMoveHand1.add(mPosition1);
+    mDynMoveShad1.add(mPosition1);
+    mDynMoveHand2.add(mPosition2);
+    mDynMoveShad2.add(mPosition2);
 
-    mMoveShad1.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -551,6 +613,7 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(12);
     resetDynamics2(12);
+    resetDynamics3(12);
 
     Static3D point1h = new Static3D(-X2+D,-Y1+D, 0);
     Static3D point2h = new Static3D( X2+D, Y1+D, 0);
@@ -561,19 +624,25 @@ class RubikControlWhole implements EffectListener
     Static3D scale2F = new Static3D(-s001,s001,s001);
     Static3D point2H = new Static3D(-s033,s033,s033);
 
+    mPosition1.set(scale1S);
+    mPosition2.set(scale2S);
+
+    mDyn1.add(scale1S);
+    mDyn1.add(scale1F);
+    mDyn2.add(scale2S);
+    mDyn2.add(scale2F);
+
     mDynMoveHand1.add(point1h);
     mDynMoveShad1.add(point1h);
     mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(scale1F);
-    mDynScaleShad1.add(scale1S);
+    mDynScaleShad1.add(mPosition1);
 
     mDynMoveHand2.add(point2h);
     mDynMoveShad2.add(point2h);
     mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(scale2F);
-    mDynScaleShad2.add(scale2S);
+    mDynScaleShad2.add(mPosition2);
 
-    mScaleShad1.notifyWhenFinished(this);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -583,6 +652,8 @@ class RubikControlWhole implements EffectListener
     {
     resetDynamics1(13);
     resetDynamics2(13);
+    resetDynamics3(13);
+    resetDynamics4(13);
 
     Static3D point1_0 = new Static3D(-X2  +D,-Y1  +D, 0);
     Static3D point11H = new Static3D(-X2    ,-Y1    , 0);
@@ -593,18 +664,25 @@ class RubikControlWhole implements EffectListener
     Static3D point13S = new Static3D(-X0+2*D,-Y1+2*D, 0);
     Static3D point1Sc = new Static3D( s033,s033,s033);
 
+    mPosition1.set(point1_0);
+    mDynMoveHand1.add(mPosition1);
+    mPosition2.set(point1_0);
+    mDynMoveShad1.add(mPosition2);
+
     mDynScaleHand1.add(point1Sc);
     mDynScaleShad1.add(point1Sc);
-    mDynMoveHand1.add(point1_0);
-    mDynMoveHand1.add(point11H);
-    mDynMoveHand1.add(point11H);
-    mDynMoveHand1.add(point12H);
-    mDynMoveHand1.add(point13H);
-    mDynMoveShad1.add(point1_0);
-    mDynMoveShad1.add(point11S);
-    mDynMoveShad1.add(point11S);
-    mDynMoveShad1.add(point12S);
-    mDynMoveShad1.add(point13S);
+
+    mDyn1.add(point1_0);
+    mDyn1.add(point11H);
+    mDyn1.add(point11H);
+    mDyn1.add(point12H);
+    mDyn1.add(point13H);
+
+    mDyn2.add(point1_0);
+    mDyn2.add(point11S);
+    mDyn2.add(point11S);
+    mDyn2.add(point12S);
+    mDyn2.add(point13S);
 
     Static3D point2_0 = new Static3D( X2  +D, Y1  +D, 0);
     Static3D point21H = new Static3D( X2    , Y1    , 0);
@@ -615,23 +693,29 @@ class RubikControlWhole implements EffectListener
     Static3D point23S = new Static3D( X0+2*D, Y1+2*D, 0);
     Static3D point2Sc= new Static3D(-s033,s033,s033);
 
+    mPosition3.set(point2_0);
+    mDynMoveHand2.add(mPosition3);
+    mPosition4.set(point2_0);
+    mDynMoveShad2.add(mPosition4);
+
     mDynScaleHand2.add(point2Sc);
     mDynScaleShad2.add(point2Sc);
-    mDynMoveHand2.add(point2_0);
-    mDynMoveHand2.add(point21H);
-    mDynMoveHand2.add(point21H);
-    mDynMoveHand2.add(point22H);
-    mDynMoveHand2.add(point23H);
-    mDynMoveShad2.add(point2_0);
-    mDynMoveShad2.add(point21S);
-    mDynMoveShad2.add(point21S);
-    mDynMoveShad2.add(point22S);
-    mDynMoveShad2.add(point23S);
-
-    mMoveHand2.notifyWhenFinished(this);
+
+    mDyn3.add(point2_0);
+    mDyn3.add(point21H);
+    mDyn3.add(point21H);
+    mDyn3.add(point22H);
+    mDyn3.add(point23H);
+
+    mDyn4.add(point2_0);
+    mDyn4.add(point21S);
+    mDyn4.add(point21S);
+    mDyn4.add(point22S);
+    mDyn4.add(point23S);
 
     mNodes[0].changeInputSurface(mTextureShad);
     mNodes[1].changeInputSurface(mTextureShad);
+    setPostFrame(true);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -649,11 +733,19 @@ class RubikControlWhole implements EffectListener
     mDyn2 = new Dynamic3D(time,0.5f);
     mDyn2.setMode(Dynamic.MODE_PATH);
     mDyn2.setConvexity(0.0f);
+    mDyn3 = new Dynamic3D(time,0.5f);
+    mDyn3.setMode(Dynamic.MODE_PATH);
+    mDyn3.setConvexity(0.0f);
+    mDyn4 = new Dynamic3D(time,0.5f);
+    mDyn4.setMode(Dynamic.MODE_PATH);
+    mDyn4.setConvexity(0.0f);
 
     mPosition1 = new Static3D(0,0,0);
     mPosition2 = new Static3D(0,0,0);
+    mPosition3 = new Static3D(0,0,0);
+    mPosition4 = new Static3D(0,0,0);
 
-    mHandPosition = new float[6];
+    tmpBuffer = new float[12];
 
     mDynMoveHand1 = new Dynamic3D(time,0.5f);
     mDynMoveHand1.setMode(Dynamic.MODE_PATH);
@@ -702,18 +794,18 @@ class RubikControlWhole implements EffectListener
     mEffects[3].apply(mMoveHand2);
 
     DistortedScreen screen = mControl.getScreen();
-    mWidth = screen.getWidth();
-    mHeight= screen.getHeight();
-
-    X0 = mWidth*0.55f;
-    X1 = mWidth*0.45f;
-    X2 = mWidth*0.35f;
-    Y1 = mHeight*0.28f;
-    D  = mWidth*0.01f;
-    s001 = mWidth*0.0001f;
-    s014 = mWidth*0.14f;
-    s033 = mWidth*0.33f;
-    F  = 0.60f;
+    int wid= screen.getWidth();
+    int hei= screen.getHeight();
+
+    X0   = wid*0.55f;
+    X1   = wid*0.45f;
+    X2   = wid*0.35f;
+    Y1   = hei*0.28f;
+    D    = wid*0.01f;
+    s001 = wid*0.0001f;
+    s014 = wid*0.14f;
+    s033 = wid*0.33f;
+    F    = 0.60f;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -787,30 +879,6 @@ class RubikControlWhole implements EffectListener
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC
 
-  public void effectFinished(long effectID)
-    {
-    switch( mCurrentStage )
-      {
-      case  1: setEffectsStage2(); break;
-      case  2: setEffectsStage3(); break;
-      case  3: setEffectsStage4(); break;
-      case  4: setEffectsStage5(); break;
-      case  5: setEffectsStage6(); break;
-      case  6: setEffectsStage7(); break;
-      case  7: setEffectsStage8(); break;
-      case  8: setEffectsStage9(); break;
-      case  9: setEffectsStage10();break;
-      case 10: setEffectsStage11();break;
-      case 11: setEffectsStage12();break;
-      case 12: setEffectsStage13();break;
-      default: mEffectID = -1;
-               mControl.effectFinished(mEffectID);
-               break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
   public void postDrawFrame(long time)
     {
     if( mLastTime<0 ) mLastTime = time;
@@ -819,25 +887,137 @@ class RubikControlWhole implements EffectListener
 
     switch( mCurrentStage )
       {
-      case  3:
-
-      mDyn1.setDebug(true);
-      boolean finished = mDyn1.get( mHandPosition,0, time, mDiffTime);
-               mPosition1.set(mHandPosition[0], mHandPosition[1], mHandPosition[2]);
-               if( finished )
+      case  1: boolean finished1_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished1_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished1_1 && finished1_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage2();
+                 }
+               break;
+      case  2: boolean finished2_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               if( finished2_1 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage3();
+                 }
+               break;
+      case  3: boolean finished3_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               if( finished3_1 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage4();
+                 }
+               break;
+      case  4: boolean finished4_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               if( finished4_1 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage5();
+                 }
+               break;
+      case  5: boolean finished5_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished5_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished5_1 && finished5_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage6();
+                 }
+               break;
+      case  6: boolean finished6_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished6_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished6_1 && finished6_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage7();
+                 }
+               break;
+      case  7: boolean finished7_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished7_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               boolean finished7_3 = mDyn3.get( tmpBuffer,6, time, mDiffTime);
+               boolean finished7_4 = mDyn4.get( tmpBuffer,9, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[ 1], tmpBuffer[ 2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[ 4], tmpBuffer[ 5]);
+               mPosition3.set(tmpBuffer[6], tmpBuffer[ 7], tmpBuffer[ 8]);
+               mPosition4.set(tmpBuffer[9], tmpBuffer[10], tmpBuffer[11]);
+               if( finished7_1 && finished7_2 && finished7_3 && finished7_4 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage8();
+                 }
+               break;
+      case  8: boolean finished8_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished8_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished8_1 && finished8_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage9();
+                 }
+               break;
+      case  9: boolean finished9_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished9_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished9_1 && finished9_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage10();
+                 }
+               break;
+      case 10: boolean finished10_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished10_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished10_1 && finished10_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage11();
+                 }
+               break;
+      case 11: boolean finished11_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished11_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished11_1 && finished11_2 )
+                 {
+                 setPostFrame(false);
+                 setEffectsStage12();
+                 }
+               break;
+      case 12: boolean finished12_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished12_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
+               if( finished12_1 && finished12_2 )
                  {
                  setPostFrame(false);
-                 effectFinished(0);
+                 setEffectsStage13();
                  }
                break;
-      case  9: boolean finished1 = mDyn1.get( mHandPosition,0, time, mDiffTime);
-               boolean finished2 = mDyn2.get( mHandPosition,3, time, mDiffTime);
-               mPosition1.set(mHandPosition[0], mHandPosition[1], mHandPosition[2]);
-               mPosition2.set(mHandPosition[3], mHandPosition[4], mHandPosition[5]);
-               if( finished1 && finished2 )
+      case 13: boolean finished13_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
+               boolean finished13_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
+               boolean finished13_3 = mDyn3.get( tmpBuffer,6, time, mDiffTime);
+               boolean finished13_4 = mDyn4.get( tmpBuffer,9, time, mDiffTime);
+               mPosition1.set(tmpBuffer[0], tmpBuffer[ 1], tmpBuffer[ 2]);
+               mPosition2.set(tmpBuffer[3], tmpBuffer[ 4], tmpBuffer[ 5]);
+               mPosition3.set(tmpBuffer[6], tmpBuffer[ 7], tmpBuffer[ 8]);
+               mPosition4.set(tmpBuffer[9], tmpBuffer[10], tmpBuffer[11]);
+               if( finished13_1 && finished13_2 && finished13_3 && finished13_4 )
                  {
                  setPostFrame(false);
-                 effectFinished(0);
+                 mEffectID = -1;
+                 mControl.effectFinished(mEffectID);
                  }
                break;
       default: android.util.Log.e("D", "WHAT? "+mCurrentStage);
