commit 5a4d4fba5178a619e1cbdb72d2e45af61a40dfb4
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Jun 10 00:18:03 2020 +0100

    Convert the PostRender to a PreRender, called before we render.
    This makes more sense as this way things are prepared for the very first render.

diff --git a/src/main/java/org/distorted/effects/BaseEffect.java b/src/main/java/org/distorted/effects/BaseEffect.java
index e20a4fc3..772bac22 100644
--- a/src/main/java/org/distorted/effects/BaseEffect.java
+++ b/src/main/java/org/distorted/effects/BaseEffect.java
@@ -29,7 +29,7 @@ import org.distorted.effects.solve.SolveEffect;
 import org.distorted.effects.win.WinEffect;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.main.R;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -197,17 +197,17 @@ public class BaseEffect
 
   ////////////////////////////////////////////////////////////////////////////////
 
-    public long startEffect(DistortedScreen screen, RubikPostRender post) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+    public long startEffect(DistortedScreen screen, RubikPreRender pre) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
       {
       Method method1 = mClass.getDeclaredMethod("create", int.class);
 
       Object value1 = method1.invoke(null,mCurrentType);
       BaseEffect baseEffect = (BaseEffect)value1;
 
-      Method method2 = mClass.getDeclaredMethod("start", int.class, DistortedScreen.class, RubikPostRender.class);
+      Method method2 = mClass.getDeclaredMethod("start", int.class, DistortedScreen.class, RubikPreRender.class);
 
       Integer translated = translatePos(mCurrentPos)+1;
-      Object value2 = method2.invoke(baseEffect,translated,screen,post);
+      Object value2 = method2.invoke(baseEffect,translated,screen,pre);
       return (Long)value2;
       }
 
diff --git a/src/main/java/org/distorted/effects/objectchange/ObjectChangeEffect.java b/src/main/java/org/distorted/effects/objectchange/ObjectChangeEffect.java
index 21ba74ca..101f3742 100644
--- a/src/main/java/org/distorted/effects/objectchange/ObjectChangeEffect.java
+++ b/src/main/java/org/distorted/effects/objectchange/ObjectChangeEffect.java
@@ -24,7 +24,7 @@ import org.distorted.library.effect.Effect;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 
 import java.lang.reflect.Method;
@@ -224,12 +224,12 @@ public abstract class ObjectChangeEffect extends BaseEffect implements EffectLis
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
-  public long start(int duration, DistortedScreen screen, RubikPostRender post)
+  public long start(int duration, DistortedScreen screen, RubikPreRender pre)
     {
     mScreen   = screen;
-    mObject[0]= post.getOldObject();
-    mObject[1]= post.getObject();
-    mListener = post;
+    mObject[0]= pre.getOldObject();
+    mObject[1]= pre.getObject();
+    mListener = pre;
     mDuration = duration;
 
     if( mObject[0]!=null )
diff --git a/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java b/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
index 6ccf9f8c..34d2125b 100644
--- a/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
+++ b/src/main/java/org/distorted/effects/scramble/ScrambleEffect.java
@@ -24,7 +24,7 @@ import org.distorted.library.effect.Effect;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 
 import java.lang.reflect.Method;
@@ -32,7 +32,7 @@ import java.util.Random;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public abstract class ScrambleEffect extends BaseEffect implements EffectListener, RubikPostRender.ActionFinishedListener
+public abstract class ScrambleEffect extends BaseEffect implements EffectListener, RubikPreRender.ActionFinishedListener
 {
   public enum Type
     {
@@ -63,7 +63,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
       }
     }
 
-  private RubikPostRender mPostRender;
+  private RubikPreRender mPreRender;
   private int mEffectReturned;
   private int mNumDoubleScramblesLeft, mNumScramblesLeft;
   private int mLastVector;
@@ -167,7 +167,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
         android.util.Log.e("effect", "ERROR: "+mNumDoubleScramblesLeft);
         }
 
-      mPostRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
+      mPreRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
       }
     else
       {
@@ -175,7 +175,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 
       if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
         {
-        mPostRender.effectFinished(FAKE_EFFECT_ID);
+        mPreRender.effectFinished(FAKE_EFFECT_ID);
         }
       }
     }
@@ -279,7 +279,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 
           if( mNumScramblesLeft==0 )
             {
-            mPostRender.effectFinished(FAKE_EFFECT_ID);
+            mPreRender.effectFinished(FAKE_EFFECT_ID);
             }
           }
 
@@ -302,7 +302,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 
           if( mNumScramblesLeft==0 )
             {
-            mPostRender.effectFinished(FAKE_EFFECT_ID);
+            mPreRender.effectFinished(FAKE_EFFECT_ID);
             }
           }
 
@@ -314,10 +314,10 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
-  public long start(int duration, DistortedScreen screen, RubikPostRender post)
+  public long start(int duration, DistortedScreen screen, RubikPreRender pre)
     {
-    mObject     = post.getObject();
-    mPostRender = post;
+    mObject     = pre.getObject();
+    mPreRender  = pre;
     mRowChances = mObject.getRowChances();
 
     mObject.solve();
@@ -325,7 +325,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
     mNumAxis    = mObject.getRotationAxis().length;
     mBasicAngle = mObject.getBasicAngle();
 
-    int numScrambles = post.getNumScrambles();
+    int numScrambles = pre.getNumScrambles();
     int dura = (int)(duration*Math.pow(numScrambles,0.6f));
     createBaseEffects(dura,numScrambles);
     createEffects    (dura,numScrambles);
diff --git a/src/main/java/org/distorted/effects/solve/SolveEffect.java b/src/main/java/org/distorted/effects/solve/SolveEffect.java
index fb067e75..632f00c1 100644
--- a/src/main/java/org/distorted/effects/solve/SolveEffect.java
+++ b/src/main/java/org/distorted/effects/solve/SolveEffect.java
@@ -24,7 +24,7 @@ import org.distorted.library.effect.Effect;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 
 import java.lang.reflect.Method;
@@ -197,11 +197,11 @@ public abstract class SolveEffect extends BaseEffect implements EffectListener
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
-  public long start(int duration, DistortedScreen screen, RubikPostRender post)
+  public long start(int duration, DistortedScreen screen, RubikPreRender pre)
     {
     mScreen   = screen;
-    mObject   = post.getObject();
-    mListener = post;
+    mObject   = pre.getObject();
+    mListener = pre;
     mDuration = duration;
 
     createEffectsPhase0(mDuration);
diff --git a/src/main/java/org/distorted/effects/win/WinEffect.java b/src/main/java/org/distorted/effects/win/WinEffect.java
index 996853dc..7fac333f 100644
--- a/src/main/java/org/distorted/effects/win/WinEffect.java
+++ b/src/main/java/org/distorted/effects/win/WinEffect.java
@@ -24,7 +24,7 @@ import org.distorted.library.effect.Effect;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.message.EffectListener;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 
 import java.lang.reflect.Method;
@@ -163,11 +163,11 @@ public abstract class WinEffect extends BaseEffect implements EffectListener
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
-  public long start(int duration, DistortedScreen screen, RubikPostRender post)
+  public long start(int duration, DistortedScreen screen, RubikPreRender pre)
     {
     mScreen   = screen;
-    mObject   = post.getObject();
-    mListener = post;
+    mObject   = pre.getObject();
+    mListener = pre;
     mDuration = duration;
 
     createEffects(mDuration);
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 0366f4d7..2a8b3605 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -107,7 +107,7 @@ public class RubikActivity extends AppCompatActivity
         if( sizeIndex>=0 && sizeIndex<sizes.length )
           {
           success = true;
-          view.getPostRender().changeObject(obj,size);
+          view.getPreRender().changeObject(obj,size);
           }
 
         }
@@ -118,7 +118,7 @@ public class RubikActivity extends AppCompatActivity
         int s = RubikStatePlay.DEF_SIZE;
 
         play.setObjectAndSize(this,obj,s);
-        view.getPostRender().changeObject(obj,s);
+        view.getPreRender().changeObject(obj,s);
         }
       }
     
@@ -150,7 +150,7 @@ public class RubikActivity extends AppCompatActivity
 
       RubikState.savePreferences(editor);
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.getPostRender().savePreferences(editor);
+      view.getPreRender().savePreferences(editor);
 
       editor.apply();
       }
@@ -174,7 +174,7 @@ public class RubikActivity extends AppCompatActivity
       RubikState.restorePreferences(preferences);
 
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.getPostRender().restorePreferences(preferences);
+      view.getPreRender().restorePreferences(preferences);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -203,16 +203,16 @@ public class RubikActivity extends AppCompatActivity
     public RubikObject getObject()
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikPostRender post = view.getPostRender();
-      return post.getObject();
+      RubikPreRender pre = view.getPreRender();
+      return pre.getObject();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public RubikPostRender getPostRender()
+    public RubikPreRender getPreRender()
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      return view.getPostRender();
+      return view.getPreRender();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -220,8 +220,8 @@ public class RubikActivity extends AppCompatActivity
     public void changeObject(RubikObjectList object, int size)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikPostRender post = view.getPostRender();
-      post.changeObject(object,size);
+      RubikPreRender pre = view.getPreRender();
+      pre.changeObject(object,size);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -229,8 +229,8 @@ public class RubikActivity extends AppCompatActivity
     public void setupObject(RubikObjectList object, int size, int[][] moves)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikPostRender post = view.getPostRender();
-      post.setupObject(object,size,moves);
+      RubikPreRender pre = view.getPreRender();
+      pre.setupObject(object,size,moves);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikPostRender.java b/src/main/java/org/distorted/main/RubikPostRender.java
deleted file mode 100644
index 41a65c73..00000000
--- a/src/main/java/org/distorted/main/RubikPostRender.java
+++ /dev/null
@@ -1,599 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube 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.                                                           //
-//                                                                                               //
-// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.main;
-
-import android.content.SharedPreferences;
-import android.os.Bundle;
-
-import org.distorted.dialogs.RubikDialogNewRecord;
-import org.distorted.dialogs.RubikDialogSolved;
-import org.distorted.effects.BaseEffect;
-import org.distorted.library.message.EffectListener;
-import org.distorted.objects.RubikObject;
-import org.distorted.objects.RubikObjectList;
-import org.distorted.scores.RubikScores;
-import org.distorted.states.RubikState;
-import org.distorted.states.RubikStateSolving;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikPostRender implements EffectListener
-  {
-  public interface ActionFinishedListener
-    {
-    void onActionFinished(long effectID);
-    }
-
-  private RubikSurfaceView mView;
-  private boolean mFinishRotation, mRemoveRotation, mRemovePatternRotation, mAddRotation,
-                  mSetQuatCurrent, mSetQuatAccumulated, mChangeObject, mSetupObject, mSolveObject,
-                  mScrambleObject, mInitializeObject, mSetTextureMap, mResetAllTextureMaps;
-  private boolean mCanRotate, mCanPlay;
-  private boolean mIsSolved;
-  private RubikObjectList mNextObject;
-  private int mNextSize;
-  private long mRotationFinishedID;
-  private long[] mEffectID;
-  private boolean mIsNewRecord;
-  private long mNewRecord;
-  private int mScreenWidth, mScreenHeight;
-  private SharedPreferences mPreferences;
-  private int[][] mNextMoves;
-  private RubikObject mOldObject, mNewObject;
-  private int mScrambleObjectNum;
-  private int mAddRotationAxis, mAddRotationRowBitmap, mAddRotationAngle;
-  private long mAddRotationDuration;
-  private ActionFinishedListener mAddActionListener;
-  private long mAddRotationID, mRemoveRotationID;
-  private int mCubit, mFace, mNewColor;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikPostRender(RubikSurfaceView view)
-    {
-    mView = view;
-
-    mFinishRotation       = false;
-    mRemoveRotation       = false;
-    mRemovePatternRotation= false;
-    mAddRotation          = false;
-    mSetQuatCurrent       = false;
-    mSetQuatAccumulated   = false;
-    mChangeObject         = false;
-    mSetupObject          = false;
-    mSolveObject          = false;
-    mScrambleObject       = false;
-
-    mCanRotate = true;
-    mCanPlay   = true;
-
-    mOldObject = null;
-    mNewObject = null;
-
-    mScreenWidth = mScreenHeight = 0;
-    mScrambleObjectNum = 0;
-
-    mEffectID = new long[BaseEffect.Type.LENGTH];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createObjectNow(RubikObjectList object, int size, int[][] moves)
-    {
-    boolean firstTime = (mNewObject==null);
-
-    if( mOldObject!=null ) mOldObject.releaseResources();
-    mOldObject = mNewObject;
-
-    mNewObject = object.create(size, mView.getQuatCurrent(), mView.getQuatAccumulated(), moves);
-
-    if( mNewObject!=null )
-      {
-      mNewObject.createTexture();
-      mView.setMovement(object.getObjectMovementClass());
-
-      if( firstTime ) mNewObject.restorePreferences(mPreferences);
-
-      if( mScreenWidth!=0 )
-        {
-        mNewObject.recomputeScaleFactor(mScreenWidth, mScreenHeight);
-        }
-
-      mIsSolved = mNewObject.isSolved();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// do all 'adjustable' effects (SizeChange, Solve, Scramble)
-
-  private void doEffectNow(BaseEffect.Type type)
-    {
-    int index = type.ordinal();
-
-    try
-      {
-      mEffectID[index] = type.startEffect(mView.getRenderer().getScreen(),this);
-      }
-    catch( Exception ex )
-      {
-      android.util.Log.e("renderer", "exception starting effect: "+ex.getMessage());
-
-      mCanPlay   = true;
-      mCanRotate = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removeRotationNow()
-    {
-    mRemoveRotation=false;
-    mNewObject.removeRotationNow();
-
-    boolean solved = mNewObject.isSolved();
-
-    if( solved && !mIsSolved )
-      {
-      if( RubikState.getCurrentState()==RubikState.SOLV )
-        {
-        RubikStateSolving solving = (RubikStateSolving)RubikState.SOLV.getStateClass();
-        mNewRecord = solving.getRecord();
-
-        if( mNewRecord< 0 )
-          {
-          mNewRecord = -mNewRecord;
-          mIsNewRecord = false;
-          }
-        else
-          {
-          mIsNewRecord = true;
-          }
-        }
-
-      mCanRotate = false;
-      mCanPlay = false;
-      doEffectNow( BaseEffect.Type.WIN );
-      }
-    else
-      {
-      mCanRotate = true;
-      mCanPlay = true;
-      }
-
-    mIsSolved = solved;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removeRotation()
-    {
-    mRemoveRotation = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removePatternRotation()
-    {
-    mRemovePatternRotation = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removePatternRotationNow()
-    {
-    mRemovePatternRotation=false;
-    mNewObject.removeRotationNow();
-    mAddActionListener.onActionFinished(mRemoveRotationID);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addRotationNow()
-    {
-    mAddRotation = false;
-    mAddRotationID = mNewObject.addNewRotation( mAddRotationAxis, mAddRotationRowBitmap,
-                                                mAddRotationAngle, mAddRotationDuration, this);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void finishRotationNow()
-    {
-    mFinishRotation = false;
-    mCanRotate      = false;
-    mCanPlay        = false;
-    mRotationFinishedID = mNewObject.finishRotationNow(this);
-
-    if( mRotationFinishedID==0 ) // failed to add effect - should never happen
-      {
-      mCanRotate = true;
-      mCanPlay   = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void changeObjectNow()
-    {
-    mChangeObject = false;
-
-    if ( mNewObject==null || mNewObject.getObjectList()!=mNextObject || mNewObject.getSize()!=mNextSize)
-      {
-      mCanRotate= false;
-      mCanPlay  = false;
-      createObjectNow(mNextObject, mNextSize, null);
-      doEffectNow( BaseEffect.Type.SIZECHANGE );
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setupObjectNow()
-    {
-    mSetupObject = false;
-
-    if ( mNewObject==null || mNewObject.getObjectList()!=mNextObject || mNewObject.getSize()!=mNextSize)
-      {
-      mCanRotate= false;
-      mCanPlay  = false;
-      createObjectNow(mNextObject, mNextSize, mNextMoves);
-      doEffectNow( BaseEffect.Type.SIZECHANGE );
-      }
-    else
-      {
-      mNewObject.initializeObject(mNextMoves);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void scrambleObjectNow()
-    {
-    mScrambleObject = false;
-    mCanRotate      = false;
-    mCanPlay        = false;
-    mIsSolved       = false;
-    RubikScores.getInstance().incrementNumPlays();
-    doEffectNow( BaseEffect.Type.SCRAMBLE );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void solveObjectNow()
-    {
-    mSolveObject = false;
-    mCanRotate   = false;
-    mCanPlay     = false;
-    doEffectNow( BaseEffect.Type.SOLVE );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void initializeObjectNow()
-    {
-    mInitializeObject = false;
-    mNewObject.initializeObject(mNextMoves);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setTextureMapNow()
-    {
-    mSetTextureMap = false;
-
-    if( mNewObject!=null ) mNewObject.setTextureMap(mCubit,mFace,mNewColor);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetAllTextureMapsNow()
-    {
-    mResetAllTextureMaps = false;
-
-    if( mNewObject!=null ) mNewObject.resetAllTextureMaps();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setQuatCurrentNow()
-    {
-    mSetQuatCurrent = false;
-    mView.setQuatCurrent();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setQuatAccumulatedNow()
-    {
-    mSetQuatAccumulated = false;
-    mView.setQuatAccumulated();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setScreenSize(int width, int height)
-    {
-    if( mNewObject!=null )
-      {
-      mNewObject.createTexture();
-      mNewObject.recomputeScaleFactor(width,height);
-      }
-
-    mScreenHeight = height;
-    mScreenWidth  = width;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void savePreferences(SharedPreferences.Editor editor)
-    {
-    if( mNewObject!=null )
-      {
-      mNewObject.savePreferences(editor);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void restorePreferences(SharedPreferences preferences)
-    {
-    mPreferences = preferences;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void finishRotation()
-    {
-    mFinishRotation = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void changeObject(RubikObjectList object, int size)
-    {
-    if( size>0 )
-      {
-      mChangeObject = true;
-      mNextObject = object;
-      mNextSize   = size;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setupObject(RubikObjectList object, int size, int[][] moves)
-    {
-    if( size>0 )
-      {
-      mSetupObject= true;
-      mNextObject = object;
-      mNextSize   = size;
-      mNextMoves  = moves;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setTextureMap(int cubit, int face, int newColor)
-    {
-    mSetTextureMap = true;
-
-    mCubit    = cubit;
-    mFace     = face;
-    mNewColor = newColor;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean canRotate()
-    {
-    return mCanRotate;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean canPlay()
-    {
-    return mCanPlay;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setQuatCurrentOnNextRender()
-    {
-    mSetQuatCurrent = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setQuatAccumulatedOnNextRender()
-    {
-    mSetQuatAccumulated = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void postRender()
-    {
-    if( mSetQuatCurrent        ) setQuatCurrentNow();
-    if( mSetQuatAccumulated    ) setQuatAccumulatedNow();
-    if( mFinishRotation        ) finishRotationNow();
-    if( mRemoveRotation        ) removeRotationNow();
-    if( mRemovePatternRotation ) removePatternRotationNow();
-    if( mChangeObject          ) changeObjectNow();
-    if( mSetupObject           ) setupObjectNow();
-    if( mSolveObject           ) solveObjectNow();
-    if( mScrambleObject        ) scrambleObjectNow();
-    if( mAddRotation           ) addRotationNow();
-    if( mInitializeObject      ) initializeObjectNow();
-    if( mResetAllTextureMaps   ) resetAllTextureMapsNow();
-    if( mSetTextureMap         ) setTextureMapNow();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void addRotation(ActionFinishedListener listener, int axis, int rowBitmap, int angle, long duration)
-    {
-    mAddRotation = true;
-
-    mAddActionListener    = listener;
-    mAddRotationAxis      = axis;
-    mAddRotationRowBitmap = rowBitmap;
-    mAddRotationAngle     = angle;
-    mAddRotationDuration  = duration;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void initializeObject(int[][] moves)
-    {
-    mInitializeObject = true;
-    mNextMoves = moves;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void scrambleObject(int num)
-    {
-    if( mCanPlay )
-      {
-      mScrambleObject = true;
-      mScrambleObjectNum = num;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void solveObject()
-    {
-    if( mCanPlay )
-      {
-      mSolveObject = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void resetAllTextureMaps()
-    {
-    mResetAllTextureMaps = true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikObject getObject()
-    {
-    return mNewObject;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public RubikObject getOldObject()
-    {
-    return mOldObject;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getNumScrambles()
-    {
-    return mScrambleObjectNum;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void effectFinished(final long effectID)
-    {
-    if( effectID == mRotationFinishedID )
-      {
-      mRotationFinishedID = 0;
-      removeRotation();
-      }
-    else if( effectID == mAddRotationID )
-      {
-      mAddRotationID = 0;
-      mRemoveRotationID = effectID;
-      removePatternRotation();
-      }
-    else
-      {
-      for(int i=0; i<BaseEffect.Type.LENGTH; i++)
-        {
-        if( effectID == mEffectID[i] )
-          {
-          mCanRotate = true;
-          mCanPlay   = true;
-
-          if( i==BaseEffect.Type.SCRAMBLE.ordinal() )
-            {
-            final RubikActivity act = (RubikActivity)mView.getContext();
-
-            act.runOnUiThread(new Runnable()
-              {
-              @Override
-              public void run()
-                {
-                RubikState.switchState( act, RubikState.READ);
-                }
-              });
-            }
-
-          if( i==BaseEffect.Type.WIN.ordinal() )
-            {
-            if( RubikState.getCurrentState()==RubikState.SOLV )
-              {
-              final RubikActivity act = (RubikActivity)mView.getContext();
-              Bundle bundle = new Bundle();
-              bundle.putLong("time", mNewRecord );
-
-              if( mIsNewRecord )
-                {
-                RubikDialogNewRecord dialog = new RubikDialogNewRecord();
-                dialog.setArguments(bundle);
-                dialog.show( act.getSupportFragmentManager(), null);
-                }
-              else
-                {
-                RubikDialogSolved dialog = new RubikDialogSolved();
-                dialog.setArguments(bundle);
-                dialog.show( act.getSupportFragmentManager(), null);
-                }
-
-              act.runOnUiThread(new Runnable()
-                {
-                @Override
-                public void run()
-                  {
-                  RubikState.switchState( act, RubikState.DONE);
-                  }
-                });
-              }
-            }
-
-          break;
-          }
-        }
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/main/RubikPreRender.java b/src/main/java/org/distorted/main/RubikPreRender.java
new file mode 100644
index 00000000..f09a07b8
--- /dev/null
+++ b/src/main/java/org/distorted/main/RubikPreRender.java
@@ -0,0 +1,599 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube 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.                                                           //
+//                                                                                               //
+// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.main;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+import org.distorted.dialogs.RubikDialogNewRecord;
+import org.distorted.dialogs.RubikDialogSolved;
+import org.distorted.effects.BaseEffect;
+import org.distorted.library.message.EffectListener;
+import org.distorted.objects.RubikObject;
+import org.distorted.objects.RubikObjectList;
+import org.distorted.scores.RubikScores;
+import org.distorted.states.RubikState;
+import org.distorted.states.RubikStateSolving;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikPreRender implements EffectListener
+  {
+  public interface ActionFinishedListener
+    {
+    void onActionFinished(long effectID);
+    }
+
+  private RubikSurfaceView mView;
+  private boolean mFinishRotation, mRemoveRotation, mRemovePatternRotation, mAddRotation,
+                  mSetQuatCurrent, mSetQuatAccumulated, mChangeObject, mSetupObject, mSolveObject,
+                  mScrambleObject, mInitializeObject, mSetTextureMap, mResetAllTextureMaps;
+  private boolean mCanRotate, mCanPlay;
+  private boolean mIsSolved;
+  private RubikObjectList mNextObject;
+  private int mNextSize;
+  private long mRotationFinishedID;
+  private long[] mEffectID;
+  private boolean mIsNewRecord;
+  private long mNewRecord;
+  private int mScreenWidth, mScreenHeight;
+  private SharedPreferences mPreferences;
+  private int[][] mNextMoves;
+  private RubikObject mOldObject, mNewObject;
+  private int mScrambleObjectNum;
+  private int mAddRotationAxis, mAddRotationRowBitmap, mAddRotationAngle;
+  private long mAddRotationDuration;
+  private ActionFinishedListener mAddActionListener;
+  private long mAddRotationID, mRemoveRotationID;
+  private int mCubit, mFace, mNewColor;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikPreRender(RubikSurfaceView view)
+    {
+    mView = view;
+
+    mFinishRotation       = false;
+    mRemoveRotation       = false;
+    mRemovePatternRotation= false;
+    mAddRotation          = false;
+    mSetQuatCurrent       = false;
+    mSetQuatAccumulated   = false;
+    mChangeObject         = false;
+    mSetupObject          = false;
+    mSolveObject          = false;
+    mScrambleObject       = false;
+
+    mCanRotate = true;
+    mCanPlay   = true;
+
+    mOldObject = null;
+    mNewObject = null;
+
+    mScreenWidth = mScreenHeight = 0;
+    mScrambleObjectNum = 0;
+
+    mEffectID = new long[BaseEffect.Type.LENGTH];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createObjectNow(RubikObjectList object, int size, int[][] moves)
+    {
+    boolean firstTime = (mNewObject==null);
+
+    if( mOldObject!=null ) mOldObject.releaseResources();
+    mOldObject = mNewObject;
+
+    mNewObject = object.create(size, mView.getQuatCurrent(), mView.getQuatAccumulated(), moves);
+
+    if( mNewObject!=null )
+      {
+      mNewObject.createTexture();
+      mView.setMovement(object.getObjectMovementClass());
+
+      if( firstTime ) mNewObject.restorePreferences(mPreferences);
+
+      if( mScreenWidth!=0 )
+        {
+        mNewObject.recomputeScaleFactor(mScreenWidth, mScreenHeight);
+        }
+
+      mIsSolved = mNewObject.isSolved();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// do all 'adjustable' effects (SizeChange, Solve, Scramble)
+
+  private void doEffectNow(BaseEffect.Type type)
+    {
+    int index = type.ordinal();
+
+    try
+      {
+      mEffectID[index] = type.startEffect(mView.getRenderer().getScreen(),this);
+      }
+    catch( Exception ex )
+      {
+      android.util.Log.e("renderer", "exception starting effect: "+ex.getMessage());
+
+      mCanPlay   = true;
+      mCanRotate = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void removeRotationNow()
+    {
+    mRemoveRotation=false;
+    mNewObject.removeRotationNow();
+
+    boolean solved = mNewObject.isSolved();
+
+    if( solved && !mIsSolved )
+      {
+      if( RubikState.getCurrentState()==RubikState.SOLV )
+        {
+        RubikStateSolving solving = (RubikStateSolving)RubikState.SOLV.getStateClass();
+        mNewRecord = solving.getRecord();
+
+        if( mNewRecord< 0 )
+          {
+          mNewRecord = -mNewRecord;
+          mIsNewRecord = false;
+          }
+        else
+          {
+          mIsNewRecord = true;
+          }
+        }
+
+      mCanRotate = false;
+      mCanPlay = false;
+      doEffectNow( BaseEffect.Type.WIN );
+      }
+    else
+      {
+      mCanRotate = true;
+      mCanPlay = true;
+      }
+
+    mIsSolved = solved;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void removeRotation()
+    {
+    mRemoveRotation = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void removePatternRotation()
+    {
+    mRemovePatternRotation = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void removePatternRotationNow()
+    {
+    mRemovePatternRotation=false;
+    mNewObject.removeRotationNow();
+    mAddActionListener.onActionFinished(mRemoveRotationID);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void addRotationNow()
+    {
+    mAddRotation = false;
+    mAddRotationID = mNewObject.addNewRotation( mAddRotationAxis, mAddRotationRowBitmap,
+                                                mAddRotationAngle, mAddRotationDuration, this);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void finishRotationNow()
+    {
+    mFinishRotation = false;
+    mCanRotate      = false;
+    mCanPlay        = false;
+    mRotationFinishedID = mNewObject.finishRotationNow(this);
+
+    if( mRotationFinishedID==0 ) // failed to add effect - should never happen
+      {
+      mCanRotate = true;
+      mCanPlay   = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void changeObjectNow()
+    {
+    mChangeObject = false;
+
+    if ( mNewObject==null || mNewObject.getObjectList()!=mNextObject || mNewObject.getSize()!=mNextSize)
+      {
+      mCanRotate= false;
+      mCanPlay  = false;
+      createObjectNow(mNextObject, mNextSize, null);
+      doEffectNow( BaseEffect.Type.SIZECHANGE );
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupObjectNow()
+    {
+    mSetupObject = false;
+
+    if ( mNewObject==null || mNewObject.getObjectList()!=mNextObject || mNewObject.getSize()!=mNextSize)
+      {
+      mCanRotate= false;
+      mCanPlay  = false;
+      createObjectNow(mNextObject, mNextSize, mNextMoves);
+      doEffectNow( BaseEffect.Type.SIZECHANGE );
+      }
+    else
+      {
+      mNewObject.initializeObject(mNextMoves);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void scrambleObjectNow()
+    {
+    mScrambleObject = false;
+    mCanRotate      = false;
+    mCanPlay        = false;
+    mIsSolved       = false;
+    RubikScores.getInstance().incrementNumPlays();
+    doEffectNow( BaseEffect.Type.SCRAMBLE );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void solveObjectNow()
+    {
+    mSolveObject = false;
+    mCanRotate   = false;
+    mCanPlay     = false;
+    doEffectNow( BaseEffect.Type.SOLVE );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void initializeObjectNow()
+    {
+    mInitializeObject = false;
+    mNewObject.initializeObject(mNextMoves);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setTextureMapNow()
+    {
+    mSetTextureMap = false;
+
+    if( mNewObject!=null ) mNewObject.setTextureMap(mCubit,mFace,mNewColor);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void resetAllTextureMapsNow()
+    {
+    mResetAllTextureMaps = false;
+
+    if( mNewObject!=null ) mNewObject.resetAllTextureMaps();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setQuatCurrentNow()
+    {
+    mSetQuatCurrent = false;
+    mView.setQuatCurrent();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setQuatAccumulatedNow()
+    {
+    mSetQuatAccumulated = false;
+    mView.setQuatAccumulated();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setScreenSize(int width, int height)
+    {
+    if( mNewObject!=null )
+      {
+      mNewObject.createTexture();
+      mNewObject.recomputeScaleFactor(width,height);
+      }
+
+    mScreenHeight = height;
+    mScreenWidth  = width;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void savePreferences(SharedPreferences.Editor editor)
+    {
+    if( mNewObject!=null )
+      {
+      mNewObject.savePreferences(editor);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void restorePreferences(SharedPreferences preferences)
+    {
+    mPreferences = preferences;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void finishRotation()
+    {
+    mFinishRotation = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void changeObject(RubikObjectList object, int size)
+    {
+    if( size>0 )
+      {
+      mChangeObject = true;
+      mNextObject = object;
+      mNextSize   = size;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setupObject(RubikObjectList object, int size, int[][] moves)
+    {
+    if( size>0 )
+      {
+      mSetupObject= true;
+      mNextObject = object;
+      mNextSize   = size;
+      mNextMoves  = moves;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setTextureMap(int cubit, int face, int newColor)
+    {
+    mSetTextureMap = true;
+
+    mCubit    = cubit;
+    mFace     = face;
+    mNewColor = newColor;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean canRotate()
+    {
+    return mCanRotate;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean canPlay()
+    {
+    return mCanPlay;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setQuatCurrentOnNextRender()
+    {
+    mSetQuatCurrent = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setQuatAccumulatedOnNextRender()
+    {
+    mSetQuatAccumulated = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void preRender()
+    {
+    if( mSetQuatCurrent        ) setQuatCurrentNow();
+    if( mSetQuatAccumulated    ) setQuatAccumulatedNow();
+    if( mFinishRotation        ) finishRotationNow();
+    if( mRemoveRotation        ) removeRotationNow();
+    if( mRemovePatternRotation ) removePatternRotationNow();
+    if( mChangeObject          ) changeObjectNow();
+    if( mSetupObject           ) setupObjectNow();
+    if( mSolveObject           ) solveObjectNow();
+    if( mScrambleObject        ) scrambleObjectNow();
+    if( mAddRotation           ) addRotationNow();
+    if( mInitializeObject      ) initializeObjectNow();
+    if( mResetAllTextureMaps   ) resetAllTextureMapsNow();
+    if( mSetTextureMap         ) setTextureMapNow();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void addRotation(ActionFinishedListener listener, int axis, int rowBitmap, int angle, long duration)
+    {
+    mAddRotation = true;
+
+    mAddActionListener    = listener;
+    mAddRotationAxis      = axis;
+    mAddRotationRowBitmap = rowBitmap;
+    mAddRotationAngle     = angle;
+    mAddRotationDuration  = duration;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void initializeObject(int[][] moves)
+    {
+    mInitializeObject = true;
+    mNextMoves = moves;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void scrambleObject(int num)
+    {
+    if( mCanPlay )
+      {
+      mScrambleObject = true;
+      mScrambleObjectNum = num;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void solveObject()
+    {
+    if( mCanPlay )
+      {
+      mSolveObject = true;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void resetAllTextureMaps()
+    {
+    mResetAllTextureMaps = true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikObject getObject()
+    {
+    return mNewObject;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikObject getOldObject()
+    {
+    return mOldObject;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getNumScrambles()
+    {
+    return mScrambleObjectNum;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void effectFinished(final long effectID)
+    {
+    if( effectID == mRotationFinishedID )
+      {
+      mRotationFinishedID = 0;
+      removeRotation();
+      }
+    else if( effectID == mAddRotationID )
+      {
+      mAddRotationID = 0;
+      mRemoveRotationID = effectID;
+      removePatternRotation();
+      }
+    else
+      {
+      for(int i=0; i<BaseEffect.Type.LENGTH; i++)
+        {
+        if( effectID == mEffectID[i] )
+          {
+          mCanRotate = true;
+          mCanPlay   = true;
+
+          if( i==BaseEffect.Type.SCRAMBLE.ordinal() )
+            {
+            final RubikActivity act = (RubikActivity)mView.getContext();
+
+            act.runOnUiThread(new Runnable()
+              {
+              @Override
+              public void run()
+                {
+                RubikState.switchState( act, RubikState.READ);
+                }
+              });
+            }
+
+          if( i==BaseEffect.Type.WIN.ordinal() )
+            {
+            if( RubikState.getCurrentState()==RubikState.SOLV )
+              {
+              final RubikActivity act = (RubikActivity)mView.getContext();
+              Bundle bundle = new Bundle();
+              bundle.putLong("time", mNewRecord );
+
+              if( mIsNewRecord )
+                {
+                RubikDialogNewRecord dialog = new RubikDialogNewRecord();
+                dialog.setArguments(bundle);
+                dialog.show( act.getSupportFragmentManager(), null);
+                }
+              else
+                {
+                RubikDialogSolved dialog = new RubikDialogSolved();
+                dialog.setArguments(bundle);
+                dialog.show( act.getSupportFragmentManager(), null);
+                }
+
+              act.runOnUiThread(new Runnable()
+                {
+                @Override
+                public void run()
+                  {
+                  RubikState.switchState( act, RubikState.DONE);
+                  }
+                });
+              }
+            }
+
+          break;
+          }
+        }
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/main/RubikRenderer.java b/src/main/java/org/distorted/main/RubikRenderer.java
index 47cb0c61..9fd51982 100644
--- a/src/main/java/org/distorted/main/RubikRenderer.java
+++ b/src/main/java/org/distorted/main/RubikRenderer.java
@@ -59,8 +59,8 @@ public class RubikRenderer implements GLSurfaceView.Renderer
    @Override
    public void onDrawFrame(GL10 glUnused)
      {
+     mView.getPreRender().preRender();
      mScreen.render( System.currentTimeMillis() );
-     mView.getPostRender().postRender();
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -70,7 +70,7 @@ public class RubikRenderer implements GLSurfaceView.Renderer
       {
       mScreen.resize(width,height);
       mView.setScreenSize(width,height);
-      mView.getPostRender().setScreenSize(width,height);
+      mView.getPreRender().setScreenSize(width,height);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index 34005df6..984ebcc6 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -64,7 +64,7 @@ public class RubikSurfaceView extends GLSurfaceView
     private final Static4D CAMERA_POINT = new Static4D(0, 0, (float)Math.sqrt(3)*0.5f, 0);
 
     private RubikRenderer mRenderer;
-    private RubikPostRender mPostRender;
+    private RubikPreRender mPreRender;
     private RubikObjectMovement mMovement;
     private boolean mDragging, mBeginningRotation, mContinuingRotation;
     private int mScreenWidth, mScreenHeight, mScreenMin;
@@ -108,9 +108,9 @@ public class RubikSurfaceView extends GLSurfaceView
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    RubikPostRender getPostRender()
+    RubikPreRender getPreRender()
       {
-      return mPostRender;
+      return mPreRender;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -200,7 +200,7 @@ public class RubikSurfaceView extends GLSurfaceView
 
           if( mode==MODE_ROTATE )
             {
-            mBeginningRotation= mPostRender.canRotate();
+            mBeginningRotation= mPreRender.canRotate();
             }
           else if( mode==MODE_REPLACE )
             {
@@ -212,9 +212,9 @@ public class RubikSurfaceView extends GLSurfaceView
               mLastCubitFace = mMovement.getTouchedFace();
               float[] point = mMovement.getTouchedPoint3D();
               int color = solver.getCurrentColor();
-              RubikObject object = mPostRender.getObject();
+              RubikObject object = mPreRender.getObject();
               mLastCubit = object.getCubit(point);
-              mPostRender.setTextureMap( mLastCubit, mLastCubitFace, color );
+              mPreRender.setTextureMap( mLastCubit, mLastCubitFace, color );
               mLastCubitColor = SolverMain.cubitIsLocked(object.getObjectList(), object.getSize(), mLastCubit);
               }
             }
@@ -327,8 +327,8 @@ public class RubikSurfaceView extends GLSurfaceView
         {
         mLastCubitColor = -1;
 
-        mRenderer   = new RubikRenderer(this);
-        mPostRender = new RubikPostRender(this);
+        mRenderer  = new RubikRenderer(this);
+        mPreRender = new RubikPreRender(this);
 
         final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
@@ -363,7 +363,7 @@ public class RubikSurfaceView extends GLSurfaceView
                                            Static4D rotatedTouchPoint2= rotateVectorByInvertedQuat(touchPoint2, mQuatAccumulated);
 
                                            Static2D res = mMovement.newRotation(rotatedTouchPoint2);
-                                           RubikObject object = mPostRender.getObject();
+                                           RubikObject object = mPreRender.getObject();
 
                                            mCurrentAxis = (int)res.get0();
                                            float offset = res.get1();
@@ -398,12 +398,12 @@ public class RubikSurfaceView extends GLSurfaceView
                                          {
                                          float angle = continueRotation(x-mStartRotX,y-mStartRotY);
                                          mCurrentAngle = SWIPING_SENSITIVITY*angle;
-                                         mPostRender.getObject().continueRotation(mCurrentAngle);
+                                         mPreRender.getObject().continueRotation(mCurrentAngle);
                                          }
                                        else if( mDragging )
                                          {
                                          mTempCurrent.set(quatFromDrag(mX-x,y-mY));
-                                         mPostRender.setQuatCurrentOnNextRender();
+                                         mPreRender.setQuatCurrentOnNextRender();
 
                                          if( (mX-x)*(mX-x) + (mY-y)*(mY-y) > 1.0f/(DIRECTION_SENSITIVITY*DIRECTION_SENSITIVITY) )
                                            {
@@ -411,8 +411,8 @@ public class RubikSurfaceView extends GLSurfaceView
                                            mY = y;
                                            mTempAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
                                            mTempCurrent.set(0f, 0f, 0f, 1f);
-                                           mPostRender.setQuatCurrentOnNextRender();
-                                           mPostRender.setQuatAccumulatedOnNextRender();
+                                           mPreRender.setQuatCurrentOnNextRender();
+                                           mPreRender.setQuatAccumulatedOnNextRender();
                                            }
                                          }
                                        else
@@ -424,19 +424,19 @@ public class RubikSurfaceView extends GLSurfaceView
                                          {
                                          mTempAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
                                          mTempCurrent.set(0f, 0f, 0f, 1f);
-                                         mPostRender.setQuatCurrentOnNextRender();
-                                         mPostRender.setQuatAccumulatedOnNextRender();
+                                         mPreRender.setQuatCurrentOnNextRender();
+                                         mPreRender.setQuatAccumulatedOnNextRender();
                                          }
 
                                        if( mContinuingRotation )
                                          {
-                                         mPostRender.finishRotation();
+                                         mPreRender.finishRotation();
 
                                          if( RubikState.getCurrentState()==RubikState.SOLV )
                                            {
                                            RubikStateSolving solving = (RubikStateSolving)RubikState.SOLV.getStateClass();
 
-                                           int angle = mPostRender.getObject().computeNearestAngle(mCurrentAngle);
+                                           int angle = mPreRender.getObject().computeNearestAngle(mCurrentAngle);
 
                                            if( angle!=0 )
                                              solving.addMove(mCurrentAxis, mCurrentRow, angle);
@@ -444,7 +444,7 @@ public class RubikSurfaceView extends GLSurfaceView
                                          }
                                        if( mLastCubitColor>=0 )
                                          {
-                                          mPostRender.setTextureMap( mLastCubit, mLastCubitFace, mLastCubitColor );
+                                          mPreRender.setTextureMap( mLastCubit, mLastCubitFace, mLastCubitColor );
                                          }
                                        break;
          }
diff --git a/src/main/java/org/distorted/patterns/RubikPattern.java b/src/main/java/org/distorted/patterns/RubikPattern.java
index a8170bda..f45e9b53 100644
--- a/src/main/java/org/distorted/patterns/RubikPattern.java
+++ b/src/main/java/org/distorted/patterns/RubikPattern.java
@@ -19,7 +19,7 @@
 
 package org.distorted.patterns;
 
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -149,27 +149,27 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void makeMove(RubikPostRender post, int pattern)
+    void makeMove(RubikPreRender pre, int pattern)
       {
       if( !mInitialized ) initialize();
 
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = mPatterns.get(pattern);
-        if( p!=null ) p.makeMove(post);
+        if( p!=null ) p.makeMove(pre);
         }
       }
 
   /////////////////////////////////////////////////////////////
 
-    void backMove(RubikPostRender post, int pattern)
+    void backMove(RubikPreRender pre, int pattern)
       {
       if( !mInitialized ) initialize();
 
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = mPatterns.get(pattern);
-        if( p!=null ) p.backMove(post);
+        if( p!=null ) p.backMove(pre);
         }
       }
 
@@ -191,7 +191,7 @@ public class RubikPattern
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static class Pattern implements RubikPostRender.ActionFinishedListener
+  private static class Pattern implements RubikPreRender.ActionFinishedListener
     {
     private String nameStr, moveStr;
     private int[][] moves;
@@ -249,7 +249,7 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void makeMove(RubikPostRender post)
+    void makeMove(RubikPreRender pre)
       {
       if( !mInitialized ) initialize();
 
@@ -260,20 +260,20 @@ public class RubikPattern
         if( curMove>numMove )
           {
           curMove= 0;
-          post.initializeObject(null);
+          pre.initializeObject(null);
           }
         else
           {
           int axis     = moves[curMove-1][0];
 		      int rowBitmap= moves[curMove-1][1];
 		      int bareAngle= moves[curMove-1][2];
-          int angle    = bareAngle*(360/post.getObject().getBasicAngle());
+          int angle    = bareAngle*(360/pre.getObject().getBasicAngle());
           int numRot   = Math.abs(bareAngle);
 
           if( angle!=0 )
             {
             mCanRotate = false;
-            post.addRotation(this, axis, rowBitmap, angle, numRot*DURATION_MILLIS);
+            pre.addRotation(this, axis, rowBitmap, angle, numRot*DURATION_MILLIS);
             }
           else
             {
@@ -289,7 +289,7 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void backMove(RubikPostRender post)
+    void backMove(RubikPreRender pre)
       {
       if( !mInitialized ) initialize();
 
@@ -300,20 +300,20 @@ public class RubikPattern
         if( curMove<0 )
           {
           curMove=numMove;
-          post.initializeObject(moves);
+          pre.initializeObject(moves);
           }
         else
           {
           int axis     = moves[curMove][0];
 		      int rowBitmap= moves[curMove][1];
 		      int bareAngle= moves[curMove][2];
-          int angle    = bareAngle*(360/post.getObject().getBasicAngle());
+          int angle    = bareAngle*(360/pre.getObject().getBasicAngle());
           int numRot   = Math.abs(bareAngle);
 
           if( angle!=0 )
             {
             mCanRotate = false;
-            post.addRotation(this, axis, rowBitmap, -angle, numRot*DURATION_MILLIS);
+            pre.addRotation(this, axis, rowBitmap, -angle, numRot*DURATION_MILLIS);
             }
           else
             {
@@ -483,18 +483,18 @@ public class RubikPattern
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void makeMove(RubikPostRender post, int tab, int cat, int pat)
+  public void makeMove(RubikPreRender pre, int tab, int cat, int pat)
     {
     Category c = getCategory(tab,cat);
-    if( c!=null ) c.makeMove(post,pat);
+    if( c!=null ) c.makeMove(pre,pat);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void backMove(RubikPostRender post, int tab, int cat, int pat)
+  public void backMove(RubikPreRender pre, int tab, int cat, int pat)
     {
     Category c = getCategory(tab,cat);
-    if( c!=null ) c.backMove(post,pat);
+    if( c!=null ) c.backMove(pre,pat);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/states/RubikStatePattern.java b/src/main/java/org/distorted/states/RubikStatePattern.java
index efe4ca2e..c5d5fe82 100644
--- a/src/main/java/org/distorted/states/RubikStatePattern.java
+++ b/src/main/java/org/distorted/states/RubikStatePattern.java
@@ -34,7 +34,7 @@ import android.widget.TextView;
 import org.distorted.dialogs.RubikDialogPattern;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObjectList;
 import org.distorted.patterns.RubikPattern;
 import org.distorted.patterns.RubikPatternList;
@@ -200,8 +200,8 @@ public class RubikStatePattern extends RubikStateAbstract
       public void onClick(View v)
         {
         RubikPattern pattern = RubikPattern.getInstance();
-        RubikPostRender post = act.getPostRender();
-        pattern.backMove( post, mPatternOrdinal, mCategory, mPattern);
+        RubikPreRender pre = act.getPreRender();
+        pattern.backMove( pre, mPatternOrdinal, mCategory, mPattern);
         int currMove = pattern.getCurMove(mPatternOrdinal, mCategory, mPattern);
         mMovesText.setText(act.getString(R.string.mo_placeholder,currMove,mNumMoves));
         }
@@ -225,8 +225,8 @@ public class RubikStatePattern extends RubikStateAbstract
       public void onClick(View v)
         {
         RubikPattern pattern = RubikPattern.getInstance();
-        RubikPostRender post = act.getPostRender();
-        pattern.makeMove( post, mPatternOrdinal, mCategory, mPattern);
+        RubikPreRender pre = act.getPreRender();
+        pattern.makeMove( pre, mPatternOrdinal, mCategory, mPattern);
         int currMove = pattern.getCurMove(mPatternOrdinal, mCategory, mPattern);
         mMovesText.setText(act.getString(R.string.mo_placeholder,currMove,mNumMoves));
         }
diff --git a/src/main/java/org/distorted/states/RubikStatePlay.java b/src/main/java/org/distorted/states/RubikStatePlay.java
index c29854bd..80459c32 100644
--- a/src/main/java/org/distorted/states/RubikStatePlay.java
+++ b/src/main/java/org/distorted/states/RubikStatePlay.java
@@ -119,7 +119,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       @Override
       public void onClick(View view)
         {
-        if( act.getPostRender().canPlay() )
+        if( act.getPreRender().canPlay() )
           {
           int total = RubikObjectList.getTotal();
           boolean vertical = act.isVertical();
@@ -193,7 +193,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       @Override
       public void onClick(View v)
         {
-        act.getPostRender().scrambleObject(mLevelValue);
+        act.getPreRender().scrambleObject(mLevelValue);
         }
       });
     }
@@ -214,7 +214,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       @Override
       public void onClick(View v)
         {
-        act.getPostRender().solveObject();
+        act.getPreRender().solveObject();
         }
       });
     }
@@ -235,7 +235,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
       @Override
       public void onClick(View v)
         {
-        if( act.getPostRender().canPlay() ) RubikState.goBack(act);
+        if( act.getPreRender().canPlay() ) RubikState.goBack(act);
         }
       });
     }
@@ -281,7 +281,7 @@ public class RubikStatePlay extends RubikStateAbstract implements AdapterView.On
           @Override
           public void onClick(View v)
             {
-            if( act.getPostRender().canPlay() && RubikState.getCurrentState()==RubikState.PLAY )
+            if( act.getPreRender().canPlay() && RubikState.getCurrentState()==RubikState.PLAY )
               {
               FirebaseAnalytics analytics = act.getAnalytics();
 
diff --git a/src/main/java/org/distorted/states/RubikStateSolution.java b/src/main/java/org/distorted/states/RubikStateSolution.java
index a2357474..df845f6a 100644
--- a/src/main/java/org/distorted/states/RubikStateSolution.java
+++ b/src/main/java/org/distorted/states/RubikStateSolution.java
@@ -31,13 +31,13 @@ import android.widget.TextView;
 
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 import org.distorted.patterns.RubikPattern;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStateSolution extends RubikStateAbstract implements RubikPostRender.ActionFinishedListener
+public class RubikStateSolution extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
   {
   private static final int DURATION_MILLIS = 750;
 
@@ -106,8 +106,8 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
       @Override
       public void onClick(View v)
         {
-        RubikPostRender post = act.getPostRender();
-        backMove(post);
+        RubikPreRender pre = act.getPreRender();
+        backMove(pre);
         mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
         }
       });
@@ -129,8 +129,8 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
       @Override
       public void onClick(View v)
         {
-        RubikPostRender post = act.getPostRender();
-        makeMove(post);
+        RubikPreRender pre = act.getPreRender();
+        makeMove(pre);
         mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
         }
       });
@@ -174,7 +174,7 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void makeMove(RubikPostRender post)
+  private void makeMove(RubikPreRender pre)
     {
     if( mCanRotate )
       {
@@ -183,20 +183,20 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
       if( mCurrMove>mNumMoves )
         {
         mCurrMove= 0;
-        post.initializeObject(null);
+        pre.initializeObject(null);
         }
       else
         {
         int axis     = mMoves[mCurrMove-1][0];
 		    int rowBitmap= mMoves[mCurrMove-1][1];
 		    int bareAngle= mMoves[mCurrMove-1][2];
-        int angle    = bareAngle*(360/post.getObject().getBasicAngle());
+        int angle    = bareAngle*(360/pre.getObject().getBasicAngle());
         int numRot   = Math.abs(bareAngle);
 
         if( angle!=0 )
           {
           mCanRotate = false;
-          post.addRotation(this, axis, rowBitmap, angle, numRot*DURATION_MILLIS);
+          pre.addRotation(this, axis, rowBitmap, angle, numRot*DURATION_MILLIS);
           }
         else
           {
@@ -212,7 +212,7 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void backMove(RubikPostRender post)
+  private void backMove(RubikPreRender pre)
     {
     if( mCanRotate )
       {
@@ -221,20 +221,20 @@ public class RubikStateSolution extends RubikStateAbstract implements RubikPostR
       if( mCurrMove<0 )
         {
         mCurrMove=mNumMoves;
-        post.initializeObject(mMoves);
+        pre.initializeObject(mMoves);
         }
       else
         {
         int axis     = mMoves[mCurrMove][0];
 		    int rowBitmap= mMoves[mCurrMove][1];
 		    int bareAngle= mMoves[mCurrMove][2];
-        int angle    = bareAngle*(360/post.getObject().getBasicAngle());
+        int angle    = bareAngle*(360/pre.getObject().getBasicAngle());
         int numRot   = Math.abs(bareAngle);
 
         if( angle!=0 )
           {
           mCanRotate = false;
-          post.addRotation(this, axis, rowBitmap, -angle, numRot*DURATION_MILLIS);
+          pre.addRotation(this, axis, rowBitmap, -angle, numRot*DURATION_MILLIS);
           }
         else
           {
diff --git a/src/main/java/org/distorted/states/RubikStateSolver.java b/src/main/java/org/distorted/states/RubikStateSolver.java
index af29f230..15f1a48c 100644
--- a/src/main/java/org/distorted/states/RubikStateSolver.java
+++ b/src/main/java/org/distorted/states/RubikStateSolver.java
@@ -36,7 +36,7 @@ import android.widget.LinearLayout;
 import org.distorted.dialogs.RubikDialogSolverError;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 import org.distorted.solvers.ImplementedSolversList;
@@ -222,8 +222,8 @@ public class RubikStateSolver extends RubikStateAbstract
       @Override
       public void onClick(View v)
         {
-        RubikPostRender post = act.getPostRender();
-        post.resetAllTextureMaps();
+        RubikPreRender pre = act.getPreRender();
+        pre.resetAllTextureMaps();
         RubikState.goBack(act);
         }
       });
diff --git a/src/main/java/org/distorted/states/RubikStateSolving.java b/src/main/java/org/distorted/states/RubikStateSolving.java
index 40bacea4..f57fcfad 100644
--- a/src/main/java/org/distorted/states/RubikStateSolving.java
+++ b/src/main/java/org/distorted/states/RubikStateSolving.java
@@ -30,7 +30,7 @@ import android.widget.TextView;
 
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikPostRender;
+import org.distorted.main.RubikPreRender;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikObjectList;
 import org.distorted.scores.RubikScores;
@@ -41,7 +41,7 @@ import java.util.TimerTask;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class RubikStateSolving extends RubikStateAbstract implements RubikPostRender.ActionFinishedListener
+public class RubikStateSolving extends RubikStateAbstract implements RubikPreRender.ActionFinishedListener
   {
   private static final int DURATION_MILLIS = 750;
 
@@ -152,15 +152,15 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPostRe
       @Override
       public void onClick(View v)
         {
-        RubikPostRender post = act.getPostRender();
-        backMove(post);
+        RubikPreRender pre = act.getPreRender();
+        backMove(pre);
         }
       });
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void backMove(RubikPostRender post)
+  private void backMove(RubikPreRender pre)
     {
     if( mCanPrevMove )
       {
@@ -169,7 +169,7 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPostRe
       if( numMoves>0 )
         {
         Move move = mMoves.remove(numMoves-1);
-        RubikObject object = post.getObject();
+        RubikObject object = pre.getObject();
 
         int axis  = move.mAxis;
         int row   = (1<<move.mRow);
@@ -179,7 +179,7 @@ public class RubikStateSolving extends RubikStateAbstract implements RubikPostRe
         if( angle!=0 )
           {
           mCanPrevMove = false;
-          post.addRotation(this, axis, row, -angle, numRot*DURATION_MILLIS);
+          pre.addRotation(this, axis, row, -angle, numRot*DURATION_MILLIS);
 
           if( numMoves==1 )
             {
