commit 434f2f5a337134b0d9ed7da5318ecd101687a787
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue May 7 15:04:58 2019 +0100

    DistortedCube: progress with abstract Transition Effects.

diff --git a/src/main/java/org/distorted/effect/CubeEffect.java b/src/main/java/org/distorted/effect/CubeEffect.java
deleted file mode 100644
index eb4d39d9..00000000
--- a/src/main/java/org/distorted/effect/CubeEffect.java
+++ /dev/null
@@ -1,27 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2019 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.effect;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public interface CubeEffect
-  {
-
-  }
diff --git a/src/main/java/org/distorted/effect/TransitionEffect.java b/src/main/java/org/distorted/effect/TransitionEffect.java
new file mode 100644
index 00000000..f4b1605f
--- /dev/null
+++ b/src/main/java/org/distorted/effect/TransitionEffect.java
@@ -0,0 +1,92 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.effect;
+
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.library.message.EffectListener;
+import org.distorted.magic.RubikCube;
+
+import java.lang.reflect.Method;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public abstract class TransitionEffect
+  {
+  public enum Type
+    {
+    /**
+     * No transition effect at all, just detach the old cube and attach the new one straight away.
+     */
+    EMPTY      (TransitionEffectEmpty.class),
+    /**
+     * Gradually make the old cube more and more transparent until it disappears, then make the new one appear.
+     */
+    DISAPPEAR  (TransitionEffectDisappear.class);
+
+    private final Class<? extends TransitionEffect> effectClass;
+
+    Type(Class<? extends TransitionEffect> effectClass)
+      {
+      this.effectClass = effectClass;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public abstract long prepare(EffectListener listener);
+  public abstract void start(DistortedScreen screen, RubikCube oldCube, RubikCube newCube);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static TransitionEffect createEffect(Type type) throws InstantiationException, IllegalAccessException
+    {
+    return type.effectClass.newInstance();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void enableEffects()
+    {
+    Method method=null;
+
+    for(Type type: Type.values())
+      {
+      Class<? extends TransitionEffect> cls = type.effectClass;
+
+      try
+        {
+        method = cls.getMethod("enable");
+        }
+      catch(NoSuchMethodException ex)
+        {
+        android.util.Log.e("transitionEffect", "exception getting method: "+ex.getMessage());
+        }
+
+      try
+        {
+        method.invoke(null);
+        }
+      catch(Exception ex)
+        {
+        android.util.Log.e("transitionEffect", "exception invoking method: "+ex.getMessage());
+        }
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/effect/TransitionEffectDisappear.java b/src/main/java/org/distorted/effect/TransitionEffectDisappear.java
new file mode 100644
index 00000000..eac7570e
--- /dev/null
+++ b/src/main/java/org/distorted/effect/TransitionEffectDisappear.java
@@ -0,0 +1,111 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.effect;
+
+import org.distorted.library.effect.FragmentEffectAlpha;
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.library.message.EffectListener;
+import org.distorted.library.message.EffectMessage;
+import org.distorted.library.type.Dynamic1D;
+import org.distorted.library.type.Static1D;
+import org.distorted.magic.RubikCube;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class TransitionEffectDisappear extends TransitionEffect implements EffectListener
+  {
+  private static final int DURATION_IN_MILLIS = 1000;
+
+  private EffectListener mListener;
+  private FragmentEffectAlpha mDisappear, mAppear;
+  private DistortedScreen mScreen;
+  private RubikCube mOld, mNew;
+
+  private long mDisappearID;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TransitionEffectDisappear()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// enable all effects we are using in this Transition.
+// called by reflection from the parent class.
+
+  @SuppressWarnings("unused")
+  static void enable()
+    {
+    FragmentEffectAlpha.enable();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long prepare(EffectListener listener)
+    {
+    mListener = listener;
+
+    Dynamic1D disappearDyn = new Dynamic1D(DURATION_IN_MILLIS, 0.5f);  // this means - linearily change the
+    disappearDyn.add(new Static1D(1.0f));                              // alpha of the old cube from 1.0 (100%)
+    disappearDyn.add(new Static1D(0.0f));                              // to 0.0 (0%) within DURATION millisecs.
+    mDisappear = new FragmentEffectAlpha(disappearDyn);
+
+    Dynamic1D appearDyn = new Dynamic1D(DURATION_IN_MILLIS, 0.5f);     // the opposite - make the new cube
+    appearDyn.add(new Static1D(0.0f));                                 // more and more opaque.
+    appearDyn.add(new Static1D(1.0f));                                 //
+    mAppear = new FragmentEffectAlpha(appearDyn);
+
+    mDisappearID = mDisappear.getID();
+
+    return mAppear.getID();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// At the start, oldCube is attached to the screen, new is not.
+// Gradually make the old cube disappear and wait for the message that this is done.
+
+  public void start(DistortedScreen screen, RubikCube oldCube, RubikCube newCube)
+    {
+    mScreen = screen;
+    mNew    = newCube;
+    mOld    = oldCube;
+
+    mOld.apply(mDisappear);
+    mOld.registerForMessages(this);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// whenever we get the message that the old cube completely disappeared, make the new one appear
+// The calling RubikRenderer will get the message that this (final) transition is over and do whatever
+// it wants to do with this.
+
+  public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
+    {
+    if( effectID == mDisappearID )
+      {
+      mScreen.detachAll();
+      mNew.attachToScreen(mScreen);
+      mNew.apply(mAppear);
+      mOld.deregisterForMessages(this);
+      mNew.registerForMessages(mListener);
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/effect/TransitionEffectEmpty.java b/src/main/java/org/distorted/effect/TransitionEffectEmpty.java
new file mode 100644
index 00000000..a69344ad
--- /dev/null
+++ b/src/main/java/org/distorted/effect/TransitionEffectEmpty.java
@@ -0,0 +1,71 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2019 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.effect;
+
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.library.message.EffectListener;
+import org.distorted.magic.RubikCube;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class TransitionEffectEmpty extends TransitionEffect
+  {
+  private static final int FAKE_EFFECT_ID = -1;
+
+  private EffectListener mListener;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TransitionEffectEmpty()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// enable all effects we are using in this Transition (here: none).
+// called by reflection from the parent class.
+
+  @SuppressWarnings("unused")
+  static void enable()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return a fake effect ID (because there's no real effect here - empty transition!
+
+  public long prepare(EffectListener listener)
+    {
+    mListener = listener;
+    return FAKE_EFFECT_ID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// we just detach the old cube, attach the new one and manually send a message with the '-1' id
+// back to the listener (RubikRenderer)
+
+  public void start(DistortedScreen screen, RubikCube oldCube, RubikCube newCube)
+    {
+    screen.detachAll();
+    newCube.attachToScreen(screen);
+
+    mListener.effectMessage(null, FAKE_EFFECT_ID, 0);
+    }
+  }
diff --git a/src/main/java/org/distorted/magic/RubikCube.java b/src/main/java/org/distorted/magic/RubikCube.java
index 07790540..1949ba68 100644
--- a/src/main/java/org/distorted/magic/RubikCube.java
+++ b/src/main/java/org/distorted/magic/RubikCube.java
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
+import org.distorted.library.effect.Effect;
 import org.distorted.library.effect.MatrixEffectMove;
 import org.distorted.library.effect.MatrixEffectQuaternion;
 import org.distorted.library.effect.MatrixEffectRotate;
@@ -41,7 +42,7 @@ import org.distorted.library.type.Static4D;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class RubikCube
+public class RubikCube
 {
     private static final int POST_ROTATION_MILLISEC = 500;
     private static final int TEXTURE_SIZE = 100;
@@ -57,7 +58,9 @@ class RubikCube
     private Static3D[][][] mRotationAxis;
     private Dynamic1D[][][] mRotationAngle;
     private Static3D[][][] mCurrentPosition;
+    private MatrixEffectRotate[][][] mRotateEffect;
     private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
+    private Static3D mMove, mScale;
     private DistortedTexture mTexture;
     private DistortedEffects mEffectsListeningForNow;
 
@@ -66,7 +69,7 @@ class RubikCube
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    RubikCube(int size, Static3D move, Static3D scale, Static4D quatC, Static4D quatA)
+    RubikCube(int size, Static4D quatC, Static4D quatA)
       {
       mSize = size;
 
@@ -74,6 +77,9 @@ class RubikCube
       mRotationAngleMiddle = new Static1D(0);
       mRotationAngleFinal  = new Static1D(0);
 
+      mMove = new Static3D(0,0,0);
+      mScale= new Static3D(1,1,1);
+
       mRotAxis= RubikSurfaceView.VECTX;
       mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
 
@@ -84,6 +90,7 @@ class RubikCube
       mRotationAxis   = new Static3D[mSize][mSize][mSize];
       mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
       mCurrentPosition= new Static3D[mSize][mSize][mSize];
+      mRotateEffect   = new MatrixEffectRotate[mSize][mSize][mSize];
 
       Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
 
@@ -91,8 +98,8 @@ class RubikCube
       Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
 
       VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
-      MatrixEffectMove        moveEffect = new MatrixEffectMove(move);
-      MatrixEffectScale      scaleEffect = new MatrixEffectScale(scale);
+      MatrixEffectMove        moveEffect = new MatrixEffectMove(mMove);
+      MatrixEffectScale      scaleEffect = new MatrixEffectScale(mScale);
       MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
       MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
 
@@ -139,6 +146,7 @@ class RubikCube
               mRotationAngle[x][y][z]   = new Dynamic1D();
               mRotationAxis[x][y][z]    = new Static3D(1,0,0);
               mCurrentPosition[x][y][z] = new Static3D(x,y,z);
+              mRotateEffect[x][y][z]    = new MatrixEffectRotate(mRotationAngle[x][y][z], mRotationAxis[x][y][z], center);
 
               mEffects[x][y][z] = new DistortedEffects();
               mEffects[x][y][z].apply(sinkEffect);
@@ -146,7 +154,7 @@ class RubikCube
               mEffects[x][y][z].apply(scaleEffect);
               mEffects[x][y][z].apply(quatCEffect);
               mEffects[x][y][z].apply(quatAEffect);
-              mEffects[x][y][z].apply( new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], center));
+              mEffects[x][y][z].apply(mRotateEffect[x][y][z]);
               mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
               mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
               }
@@ -155,7 +163,7 @@ class RubikCube
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void attachToScreen(DistortedScreen screen)
+    public void attachToScreen(DistortedScreen screen)
       {
       for(int x=0; x<mSize; x++)
         for(int y=0; y<mSize; y++)
@@ -169,6 +177,35 @@ class RubikCube
             }
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void apply(Effect effect)
+      {
+      for(int x=0; x<mSize; x++)
+        for(int y=0; y<mSize; y++)
+          for(int z=0; z<mSize; z++)
+            {
+            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
+              {
+              mEffects[x][y][z].apply(effect);
+              }
+            }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void registerForMessages(EffectListener listener)
+      {
+      mEffects[0][0][0].registerForMessages(listener);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void deregisterForMessages(EffectListener listener)
+      {
+      mEffects[0][0][0].deregisterForMessages(listener);
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
 // Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
@@ -242,11 +279,12 @@ class RubikCube
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void finishRotationNow(EffectListener listener)
+    long finishRotationNow(EffectListener listener)
       {
       boolean first = true;
       float startingAngle = mRotationAngleStatic.get1();
       int nearestAngleInDegrees = computeNearestAngle(startingAngle);
+      long effectID=0;
 
       mRotationAngleFinal.set1(nearestAngleInDegrees);
       mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
@@ -267,9 +305,12 @@ class RubikCube
                   first = false;
                   mEffectsListeningForNow = mEffects[x][y][z];
                   mEffectsListeningForNow.registerForMessages(listener);
+                  effectID = mRotateEffect[x][y][z].getID();
                   }
                 }
               }
+
+      return effectID;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -411,10 +452,13 @@ class RubikCube
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    float getTextureSize()
-      {
-      return TEXTURE_SIZE;
-      }
+   void recomputeScaleFactor(int screenWidth, int screenHeight, float size)
+     {
+     float scaleFactor = size/(TEXTURE_SIZE*mSize);
+
+     mMove.set( (screenWidth-scaleFactor*TEXTURE_SIZE)/2 , (screenHeight-scaleFactor*TEXTURE_SIZE)/2 , -scaleFactor*TEXTURE_SIZE/2 );
+     mScale.set(scaleFactor,scaleFactor,scaleFactor);
+     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/main/java/org/distorted/magic/RubikRenderer.java b/src/main/java/org/distorted/magic/RubikRenderer.java
index 782543a4..94672c7a 100644
--- a/src/main/java/org/distorted/magic/RubikRenderer.java
+++ b/src/main/java/org/distorted/magic/RubikRenderer.java
@@ -21,12 +21,12 @@ package org.distorted.magic;
 
 import android.opengl.GLSurfaceView;
 
+import org.distorted.effect.TransitionEffect;
 import org.distorted.library.effect.VertexEffectSink;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
 import org.distorted.library.message.EffectMessage;
-import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
 import javax.microedition.khronos.egl.EGLConfig;
@@ -41,14 +41,14 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
     private RubikSurfaceView mView;
     private DistortedScreen mScreen;
-    private Static3D mMove, mScale;
     private Static4D mQuatCurrent, mQuatAccumulated;
     private Static4D mTempCurrent, mTempAccumulated;
     private float mCubeSizeInScreenSpace;
     private int mNextCubeSize;
+    private long mRotationFinishedID, mTransitionEffectID;
     private boolean mFinishRotation, mRemoveRotation, mFinishDragCurrent, mFinishDragAccumulated;
-    private boolean mCanRotate;
-    private RubikCube mCube;
+    private boolean mCanRotate, mCanDrag;
+    private RubikCube mOldCube, mNewCube;
 
     private int mScreenWidth, mScreenHeight;
 
@@ -60,6 +60,9 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
       mScreen = new DistortedScreen();
 
+      mOldCube = null;
+      mNewCube = null;
+
       mTempCurrent     = new Static4D(0,0,0,1);
       mTempAccumulated = initializeQuat();
       mQuatCurrent     = new Static4D(0,0,0,1);
@@ -67,9 +70,6 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
       mScreenWidth = mScreenHeight = 0;
 
-      mMove  = new Static3D(0,0,0);
-      mScale = new Static3D(1,1,1);
-
       mFinishRotation        = false;
       mRemoveRotation        = false;
       mFinishDragCurrent     = false;
@@ -78,6 +78,7 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
       mNextCubeSize= 0;
 
       mCanRotate = true;
+      mCanDrag   = true;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -104,24 +105,34 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
         {
         mCanRotate = false;
         mFinishRotation=false;
-        mCube.finishRotationNow(this);
+        mRotationFinishedID = mNewCube.finishRotationNow(this);
         }
 
       if( mRemoveRotation )
         {
         mRemoveRotation=false;
-        mCube.removeRotationNow(this);
+        mNewCube.removeRotationNow(this);
         mCanRotate = true;
         }
 
       if( mNextCubeSize!=0 )
         {
+        mCanDrag   = false;
+        mCanRotate = false;
         createCubeNow(mNextCubeSize);
-        mScreen.detachAll();
-        mCube.attachToScreen(mScreen);
+
+        try
+          {
+          TransitionEffect effect = TransitionEffect.createEffect(TransitionEffect.Type.DISAPPEAR);
+          mTransitionEffectID = effect.prepare(this);
+          effect.start(mScreen,mOldCube,mNewCube);
+          }
+        catch(Exception ex)
+          {
+          android.util.Log.e("Renderer", "failed to create TransitionEffect, exception: "+ex.getMessage());
+          }
+
         mNextCubeSize = 0;
-        mCanRotate = true;   // it can happen that we have just changed the size of the cube while
-                             // finishing rotation and never removed it!
         }
       }
 
@@ -130,10 +141,15 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
    public void effectMessage(final EffectMessage em, final long effectID, final long objectID)
      {
-     switch(em)
-        {
-        case EFFECT_FINISHED: mRemoveRotation = true; break;
-        }
+     if(      effectID == mRotationFinishedID )
+       {
+       mRemoveRotation = true;
+       }
+     else if( effectID == mTransitionEffectID )
+       {
+       mCanRotate = true;
+       mCanDrag   = true;
+       }
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -158,11 +174,12 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
     
     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
       {
-      mCube.createTexture();
+      mNewCube.createTexture();
       mScreen.detachAll();
-      mCube.attachToScreen(mScreen);
+      mNewCube.attachToScreen(mScreen);
 
       VertexEffectSink.enable();
+      TransitionEffect.enableEffects();
 
       try
         {
@@ -201,13 +218,15 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
    void createCubeNow(int newSize)
      {
-     int oldSize = mCube==null ? 0 : mCube.getSize();
+     int oldSize = mNewCube==null ? 0 : mNewCube.getSize();
 
      if( oldSize!=newSize )
        {
-       if( mCube!=null ) mCube.releaseResources();
-       mCube = new RubikCube(newSize, mMove, mScale, mQuatCurrent, mQuatAccumulated);
-       mCube.createTexture();
+       if( mOldCube!=null ) mOldCube.releaseResources();
+       mOldCube = mNewCube;
+
+       mNewCube = new RubikCube(newSize, mQuatCurrent, mQuatAccumulated);
+       mNewCube.createTexture();
 
        if( mScreenWidth!=0 )
          {
@@ -221,11 +240,7 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
    private void recomputeScaleFactor(int screenWidth, int screenHeight)
      {
      mCubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
-     float texSize = mCube.getTextureSize();
-     float scaleFactor = mCubeSizeInScreenSpace/(texSize*mCube.getSize());
-
-     mMove.set( (screenWidth-scaleFactor*texSize)/2 , (screenHeight-scaleFactor*texSize)/2 , -scaleFactor*texSize/2 );
-     mScale.set(scaleFactor,scaleFactor,scaleFactor);
+     mNewCube.recomputeScaleFactor(screenWidth, screenHeight, mCubeSizeInScreenSpace);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -249,11 +264,18 @@ class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
      return mCanRotate;
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   boolean canDrag()
+     {
+     return mCanDrag;
+     }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    RubikCube getCube()
      {
-     return mCube;
+     return mNewCube;
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/magic/RubikSurfaceView.java b/src/main/java/org/distorted/magic/RubikSurfaceView.java
index d95de4f8..b15825ff 100644
--- a/src/main/java/org/distorted/magic/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/magic/RubikSurfaceView.java
@@ -115,7 +115,7 @@ class RubikSurfaceView extends GLSurfaceView
                                          }
                                        else
                                          {
-                                         mDragging           = true;
+                                         mDragging           = mRenderer.canDrag();
                                          mBeginningRotation  = false;
                                          mContinuingRotation = false;
                                          }
