commit 2afc67543cdfccc56e06624a10138d0ace47f363
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Oct 5 11:58:07 2021 +0200

    Move RubikControl to objectlib (as 'ObjectAutomator')
    Hide ObjectPreRender inside objectlib and move its APi to ObjectControl.

diff --git a/src/main/java/org/distorted/control/RubikControl.java b/src/main/java/org/distorted/control/RubikControl.java
deleted file mode 100644
index 77cbf2c4..00000000
--- a/src/main/java/org/distorted/control/RubikControl.java
+++ /dev/null
@@ -1,235 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2021 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.control;
-
-import org.distorted.objectlib.helpers.BlockController;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.main.DistortedScreen;
-import org.distorted.library.message.EffectListener;
-import org.distorted.library.type.Static4D;
-
-import org.distorted.objectlib.main.TwistyObject;
-
-import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikSurfaceView;
-
-import java.lang.ref.WeakReference;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class RubikControl implements EffectListener
-  {
-  private static RubikControl mControl;
-
-  WeakReference<RubikActivity> mRefAct;
-  boolean mWholeReturned, mRotateReturned;
-
-  RubikControlWhole mWhole;
-  RubikControlRotate mRotate;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedScreen getScreen()
-    {
-    RubikActivity act = mRefAct.get();
-    return act!=null ? act.getScreen() : null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  TwistyObject getObject()
-    {
-    RubikActivity act = mRefAct.get();
-    return act!=null ? act.getObject() : null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Static4D getCurrQuat()
-    {
-    RubikActivity act = mRefAct.get();
-    return act!=null ? act.getCurrQuat() : null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikSurfaceView getSurfaceView()
-    {
-    RubikActivity act = mRefAct.get();
-    return act!=null ? act.getSurfaceView() : null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addWholeObjects()
-    {
-    DistortedScreen screen = getScreen();
-    DistortedNode[] nodes = mWhole.getNodes();
-
-    if( screen!=null && nodes!=null )
-      {
-      for (DistortedNode node : nodes) screen.attach(node);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removeWholeObjects()
-    {
-    DistortedScreen screen = getScreen();
-    DistortedNode[] nodes = mWhole.returnNodes();
-
-    if( screen!=null && nodes!=null )
-      {
-      for (DistortedNode node : nodes) screen.detach(node);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addRotateObjects()
-    {
-    DistortedScreen screen = getScreen();
-    DistortedNode[] screenNodes = mRotate.getScreenNodes();
-
-    if( screen!=null && screenNodes!=null )
-      {
-      for (DistortedNode node : screenNodes) screen.attach(node);
-      }
-
-    DistortedNode object = getObject();
-    DistortedNode[] objectNodes = mRotate.getObjectNodes();
-
-    if( object!=null && objectNodes!=null )
-      {
-      for (DistortedNode node : objectNodes) object.attach(node);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void removeRotateObjects()
-    {
-    DistortedScreen screen = getScreen();
-    DistortedNode[] screenNodes = mRotate.returnScreenNodes();
-
-    if( screen!=null && screenNodes!=null )
-      {
-      for (DistortedNode node : screenNodes) screen.detach(node);
-      }
-
-    DistortedNode object = getObject();
-    DistortedNode[] objectNodes = mRotate.returnObjectNodes();
-
-    if( object!=null && objectNodes!=null )
-      {
-      for (DistortedNode node : objectNodes) object.detach(node);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void finishWhole()
-    {
-    removeWholeObjects();
-
-    mWholeReturned = true;
-
-    if( !mRotateReturned ) addRotateObjects();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void finishRotate()
-    {
-    removeRotateObjects();
-
-    mRotateReturned= true;
-
-    RubikActivity act = mRefAct.get();
-    if( act!=null ) act.unblockEverything();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikActivity getActivity()
-    {
-    return mRefAct.get();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private RubikControl()
-    {
-    mWhole = new RubikControlWhole(this);
-    mRotate= new RubikControlRotate(this);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC
-
-  public static RubikControl getInstance()
-    {
-    if( mControl==null ) mControl = new RubikControl();
-
-    return mControl;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void postDrawFrame(long time)
-    {
-    mWhole.postDrawFrame(time);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void effectFinished(long effectID)
-    {
-    if( effectID==mWhole.getEffectID()  ) finishWhole();
-    if( effectID==mRotate.getEffectID() ) finishRotate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void animateAll(RubikActivity act)
-    {
-    act.blockEverything(BlockController.CONTROL_PLACE_0);
-    mRefAct = new WeakReference<>(act);
-
-    mWholeReturned = false;
-    mRotateReturned= false;
-
-    addWholeObjects();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void animateRotate(RubikActivity act)
-    {
-    act.blockEverything(BlockController.CONTROL_PLACE_1);
-    mRefAct = new WeakReference<>(act);
-
-    mWholeReturned = true;
-    mRotateReturned= false;
-
-    addRotateObjects();
-    }
-  }
diff --git a/src/main/java/org/distorted/control/RubikControlRotate.java b/src/main/java/org/distorted/control/RubikControlRotate.java
deleted file mode 100644
index e4fd76c1..00000000
--- a/src/main/java/org/distorted/control/RubikControlRotate.java
+++ /dev/null
@@ -1,372 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2021 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.control;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import org.distorted.library.effect.MatrixEffectQuaternion;
-import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.main.DistortedScreen;
-import org.distorted.library.main.DistortedTexture;
-import org.distorted.library.mesh.MeshQuad;
-import org.distorted.library.message.EffectListener;
-import org.distorted.library.type.Dynamic;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-import org.distorted.library.main.QuatHelper;
-
-import org.distorted.objectlib.main.TwistyObject;
-
-import org.distorted.main.R;
-import org.distorted.main.RubikActivity;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikControlRotate implements EffectListener
-  {
-  private static final int NUM_SCREEN = 0;
-  private static final int NUM_OBJECT = 1;
-
-  private static Static4D INIT_QUAT;
-
-  private final RubikControl mControl;
-  private DistortedEffects[] mScreenEffects, mObjectEffects;
-  private DistortedNode[] mScreenNodes, mObjectNodes;
-  private long mScreenEffectID, mObjectEffectID, mCubeEffectID;
-  private Static4D mObjRotQuat;
-
-  private Dynamic3D mDynamic3;
-  private Dynamic4D mDynamic4;
-  private MatrixEffectScale mScale;
-  private MatrixEffectQuaternion mQuaternion;
-  private MeshQuad mScreenQuad, mObjectQuad;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private Bitmap openBitmap(RubikActivity act, int resource)
-     {
-     try( InputStream is = act.getResources().openRawResource(resource) )
-       {
-       return BitmapFactory.decodeStream(is);
-       }
-     catch( IOException e )
-       {
-       // ignore
-       }
-
-     return null;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void computeInitQuat()
-    {
-    double alphaZ = 0;//-Math.PI* 0.1250f;
-    double alphaY = 0;//-Math.PI* 0.0625f;
-
-    alphaY /= 2;
-    alphaZ /= 2;
-
-    float sinZ = (float)Math.sin(alphaZ);
-    float cosZ = (float)Math.cos(alphaZ);
-    float sinY = (float)Math.sin(alphaY);
-    float cosY = (float)Math.cos(alphaY);
-
-    Static4D qZ = new Static4D(0,0,sinZ,cosZ);
-    Static4D qY = new Static4D(0,sinY,0,cosY);
-
-    INIT_QUAT = QuatHelper.quatMultiply(qY,qZ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Take 3D vector 'ax', rotate it by quaternion 'objQuat' to get vector V1.
-// Take 3D vector (1,0,0) and rotate it by INIT_QUAT to get vector V2.
-// Return a quaternion Q such that if we rotate V1 by Q, we get V2.
-
-  private Static4D computeQuat(Static3D ax, Static4D objQuat, float x, float y, float z)
-    {
-    Static4D ax4D = new Static4D( ax.get0(), ax.get1(), ax.get2(), 0);
-    Static4D axRo = QuatHelper.rotateVectorByQuat(ax4D,objQuat);
-    return QuatHelper.retRotationQuat(axRo.get0(),axRo.get1(),axRo.get2(),x,y,z);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void computeRotQuat()
-    {
-    TwistyObject object = mControl.getObject();
-    Static3D[] axis = object.getRotationAxis();
-    int chosen=-1,numAxis = axis.length;
-    float cos,maxCos = -1.0f;
-    Static4D quat;
-
-    Static4D objCurrQuat = mControl.getCurrQuat();
-    Static4D axisX = new Static4D(1,0,0,0);
-    Static4D rotAxisX = QuatHelper.rotateVectorByQuat(axisX,INIT_QUAT);
-
-    float axX = rotAxisX.get0();
-    float axY = rotAxisX.get1();
-    float axZ = rotAxisX.get2();
-
-    for (int a=0; a<numAxis; a++)
-      {
-      quat = computeQuat(axis[a],objCurrQuat,axX,axY,axZ);
-      cos = quat.get3();
-
-android.util.Log.e("D", "axis="+a+" "+quat.get0()+" "+quat.get1()+" "+quat.get2()+" "+quat.get3() );
-
-      if (cos > maxCos)
-        {
-        maxCos = cos;
-        chosen = a;
-        if( mObjRotQuat==null ) mObjRotQuat = new Static4D(quat);
-        else mObjRotQuat.set(quat);
-        }
-      }
-
-    android.util.Log.e("D", "axis chosen: "+chosen);
-
-
-android.util.Log.e("D", mObjRotQuat.get0()+" "+mObjRotQuat.get1()+" "+mObjRotQuat.get2()+" "+mObjRotQuat.get3() );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setScreenEffectsStage1()
-    {
-    mDynamic3.resetToBeginning();
-    mScale.notifyWhenFinished(mControl);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// the whole cube rotates so that its axis is alignled according to the Euler angles defined in
-// computeInitQuat()
-
-  private void setObjectEffectsStage1()
-    {
-    TwistyObject obj = mControl.getObject();
-    obj.apply(mQuaternion,0);
-    mDynamic4.resetToBeginning();
-    mQuaternion.notifyWhenFinished(this);
-
-
-Static4D d = mDynamic4.getPoint(1);
-
-android.util.Log.e("D", "set: "+d.get0()+" "+d.get1()+" "+d.get2()+" "+d.get3() );
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setObjectEffectsStage2()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createScreenEffects()
-    {
-    mScreenEffects   = new DistortedEffects[NUM_SCREEN];
-    mScreenEffects[0]= new DistortedEffects();
-
-    DistortedScreen screen = mControl.getScreen();
-    int wid = screen.getWidth();
-
-    Static3D scaleStart= new Static3D(1,1,1);
-    Static3D scaleEnd  = new Static3D(wid,wid,wid);
-
-    mDynamic3 = new Dynamic3D(10000,0.5f);
-    mDynamic3.add(scaleStart);
-    mDynamic3.add(scaleEnd  );
-    mDynamic3.add(scaleStart);
-    mDynamic3.setMode(Dynamic.MODE_PATH);
-
-    mScale = new MatrixEffectScale(mDynamic3);
-    mScreenEffectID = mScale.getID();
-    mScreenEffects[0].apply(mScale);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createObjectEffects()
-    {
-    mObjectEffects   = new DistortedEffects[NUM_OBJECT];
-    mObjectEffects[0]= new DistortedEffects();
-
-    mDynamic4 = new Dynamic4D(5000,0.5f);
-    mDynamic4.add(new Static4D(0,0,0,1));
-    mDynamic4.add(mObjRotQuat);
-    mDynamic4.add(mObjRotQuat);
-    mDynamic4.add(mObjRotQuat);
-    mDynamic4.setMode(Dynamic.MODE_PATH);
-
-android.util.Log.e("D", "create: "+mObjRotQuat.get0()+" "+mObjRotQuat.get1()+" "+mObjRotQuat.get2()+" "+mObjRotQuat.get3() );
-
-TwistyObject obj = mControl.getObject();
-Static3D ax = obj.getRotationAxis()[0];
-Static4D axis = new Static4D(ax.get0(), ax.get1(), ax.get2(), 0);
-
-Static4D v1 = QuatHelper.rotateVectorByQuat( new Static4D(1,0,0,0), INIT_QUAT);
-Static4D v2 = QuatHelper.rotateVectorByQuat( axis                 , mObjRotQuat);
-
-android.util.Log.e("D", "v1: "+v1.get0()+" "+v1.get1()+" "+v1.get2()+" "+v1.get3());
-android.util.Log.e("D", "v2: "+v2.get0()+" "+v2.get1()+" "+v2.get2()+" "+v2.get3());
-android.util.Log.e("D", "ax: "+ax.get0()+" "+ax.get1()+" "+ax.get2());
-
-
-    Static3D center = new Static3D(0,0,0);
-    mQuaternion = new MatrixEffectQuaternion(mDynamic4, center);
-    mCubeEffectID = mQuaternion.getID();
-
-    DistortedScreen screen = mControl.getScreen();
-    int wid = screen.getWidth();
-    Static3D scaleFactor = new Static3D(wid,wid*0.1f,10);
-    MatrixEffectScale scale = new MatrixEffectScale(scaleFactor);
-    MatrixEffectQuaternion quat = new MatrixEffectQuaternion(INIT_QUAT,center);
-
-    mObjectEffects[0].apply(scale);
-    mObjectEffects[0].apply(quat);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createScreenNodes()
-    {
-    if( mScreenNodes==null )
-      {
-      mScreenNodes= new DistortedNode[NUM_SCREEN];
-      mScreenQuad = new MeshQuad();
-      }
-
-    DistortedTexture texture = new DistortedTexture();
-    texture.setColorARGB(0xff00ff00);
-    mScreenNodes[0] = new DistortedNode(texture, mScreenEffects[0], mScreenQuad);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createObjectNodes()
-    {
-    if( mObjectNodes==null )
-      {
-      mObjectNodes= new DistortedNode[NUM_OBJECT];
-      mObjectQuad = new MeshQuad();
-      }
-
-    RubikActivity act = mControl.getActivity();
-
-    if( act!=null )
-      {
-      Bitmap bmpArrow = openBitmap(act, R.drawable.ui_axis_arrow);
-      DistortedTexture textureArrow = new DistortedTexture();
-
-      if( bmpArrow!=null ) textureArrow.setTexture(bmpArrow);
-
-      mObjectNodes[0] = new DistortedNode(textureArrow, mObjectEffects[0], mObjectQuad);
-      }
-    else
-      {
-      android.util.Log.e("D", "Activity NULL!!");
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  long getEffectID()
-    {
-    return mObjectEffectID;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode[] getScreenNodes()
-    {
-    if( NUM_SCREEN>0 )
-      {
-      if( mScreenEffects==null ) createScreenEffects();
-      createScreenNodes();
-      setScreenEffectsStage1();
-      }
-
-    return mScreenNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode[] returnScreenNodes()
-    {
-    return mScreenNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   DistortedNode[] getObjectNodes()
-    {
-    if( NUM_OBJECT>0 )
-      {
-      if( INIT_QUAT==null ) computeInitQuat();
-      computeRotQuat();
-      if( mObjectEffects==null ) createObjectEffects();
-      createObjectNodes();
-      setObjectEffectsStage1();
-      }
-
-    return mObjectNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   DistortedNode[] returnObjectNodes()
-    {
-    return mObjectNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikControlRotate(RubikControl control)
-    {
-    mControl = control;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void effectFinished(long effectID)
-    {
-    if( effectID==mCubeEffectID)
-      {
-      setObjectEffectsStage2();
-
-      TwistyObject obj = mControl.getObject();
-      obj.remove(mCubeEffectID);
-      mObjectEffectID = -1;
-      mControl.effectFinished(mObjectEffectID);
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/control/RubikControlWhole.java b/src/main/java/org/distorted/control/RubikControlWhole.java
deleted file mode 100644
index 6945ba05..00000000
--- a/src/main/java/org/distorted/control/RubikControlWhole.java
+++ /dev/null
@@ -1,987 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2021 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.control;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import org.distorted.library.effect.MatrixEffectMove;
-import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.main.DistortedScreen;
-import org.distorted.library.main.DistortedTexture;
-import org.distorted.library.mesh.MeshQuad;
-import org.distorted.library.type.Dynamic;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Static3D;
-
-import org.distorted.main.R;
-import org.distorted.main.RubikActivity;
-import org.distorted.main.RubikSurfaceView;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class RubikControlWhole
-  {
-  private static final int NUM_NODE = 4;
-  private static final int NUM_EFFE = 4;
-
-  private static final int D1 = 2600; // time it takes for the finger to appear
-  private static final int D2 =  250; // finger press
-  private static final int D3 =10000; // finger triangle
-  private static final int D4 = 3000; // fingers approach
-  private static final int D5 = 8000; // fingers circle
-
-  private static final int[] DUR = { D1, D2, D3, D2, D1/4, 3*D1/4, D1/4, D2, D4, D5, D4, D2, D1 };
-  private static final int[] DYN = { 2, 1, 1, 1, 2, 2, 4, 2, 2, 2, 2, 2, 4};
-
-  private float X0, X1, X2, Y1, D, s001, s014, s033, F;
-  private int mWidth, mHeight;
-
-  private final RubikControl mControl;
-  private DistortedEffects[] mEffects;
-  private DistortedNode[] mNodes;
-  private long mEffectID;
-  private int mCurrentStage;
-
-  private MeshQuad mQuad;
-  private DistortedTexture mTextureShad, mTextureCirc;
-  private Dynamic3D mDynMoveHand1, mDynMoveShad1;
-  private Dynamic3D mDynScaleHand1, mDynScaleShad1;
-  private Dynamic3D mDynMoveHand2, mDynMoveShad2;
-  private Dynamic3D mDynScaleHand2, mDynScaleShad2;
-  private Dynamic3D mDyn1, mDyn2, mDyn3, mDyn4;
-  private Static3D mPosition1, mPosition2, mPosition3, mPosition4;
-  private float[] tmpBuffer;
-  private long mLastTime, mDiffTime;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private void setPostFrame(boolean on)
-     {
-     RubikActivity act = mControl.getActivity();
-     act.setControlState(on);
-     mLastTime = -1;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private Bitmap openBitmap(RubikActivity act, int resource)
-     {
-     try( InputStream is = act.getResources().openRawResource(resource) )
-       {
-       return BitmapFactory.decodeStream(is);
-       }
-     catch( IOException e )
-       {
-       // ignore
-       }
-
-     return null;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetDynamics1(int stage)
-    {
-    int dur = DUR[stage-1];
-
-    mDynMoveHand1.removeAll();
-    mDynMoveShad1.removeAll();
-    mDynScaleHand1.removeAll();
-    mDynScaleShad1.removeAll();
-
-    mDynMoveHand1.setDuration(dur);
-    mDynMoveShad1.setDuration(dur);
-    mDynScaleHand1.setDuration(dur);
-    mDynScaleShad1.setDuration(dur);
-    mDynMoveHand1.resetToBeginning();
-    mDynMoveShad1.resetToBeginning();
-    mDynScaleHand1.resetToBeginning();
-    mDynScaleShad1.resetToBeginning();
-
-    mCurrentStage = stage;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetDynamics2(int stage)
-    {
-    int dur = DUR[stage-1];
-
-    mDynMoveHand2.removeAll();
-    mDynMoveShad2.removeAll();
-    mDynScaleHand2.removeAll();
-    mDynScaleShad2.removeAll();
-
-    mDynMoveHand2.setDuration(dur);
-    mDynMoveShad2.setDuration(dur);
-    mDynScaleHand2.setDuration(dur);
-    mDynScaleShad2.setDuration(dur);
-    mDynMoveHand2.resetToBeginning();
-    mDynMoveShad2.resetToBeginning();
-    mDynScaleHand2.resetToBeginning();
-    mDynScaleShad2.resetToBeginning();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetDynamics3(int stage)
-    {
-    int dur = DUR[stage-1];
-
-    mDyn1.removeAll();
-    mDyn2.removeAll();
-    mDyn1.setDuration(dur);
-    mDyn2.setDuration(dur);
-    mDyn1.resetToBeginning();
-    mDyn2.resetToBeginning();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetDynamics4(int stage)
-    {
-    int dur = DUR[stage-1];
-
-    mDyn3.removeAll();
-    mDyn4.removeAll();
-    mDyn3.setDuration(dur);
-    mDyn4.setDuration(dur);
-    mDyn3.resetToBeginning();
-    mDyn4.resetToBeginning();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// first finger appears and approaches the screen
-
-  private void setEffectsStage1()
-    {
-    resetDynamics1(1);
-    resetDynamics2(1);
-    resetDynamics3(1);
-
-    Static3D point0h = new Static3D(-X0    ,-Y1    , 0);
-    Static3D point1h = new Static3D(-X1    ,-Y1    , 0);
-    Static3D point2h = new Static3D(-X2    ,-Y1    , 0);
-    Static3D point3h = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D point0s = new Static3D(-X0+2*D,-Y1+2*D, 0);
-    Static3D point1s = new Static3D(-X1+2*D,-Y1+2*D, 0);
-    Static3D point2s = new Static3D(-X2+2*D,-Y1+2*D, 0);
-    Static3D point3s = new Static3D(-X2  +D,-Y1  +D, 0);
-
-    Static3D pointSc = new Static3D(s033,s033,s033);
-
-    mDyn1.add(point0h);
-    mDyn1.add(point1h);
-    mDyn1.add(point2h);
-    mDyn1.add(point2h);
-    mDyn1.add(point3h);
-    mDyn2.add(point0s);
-    mDyn2.add(point1s);
-    mDyn2.add(point2s);
-    mDyn2.add(point2s);
-    mDyn2.add(point3s);
-
-    mPosition1.set(point0h);
-    mPosition2.set(point0s);
-
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition2);
-
-    mDynScaleHand1.add(pointSc);
-    mDynScaleShad1.add(pointSc);
-
-    setPostFrame(true);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// first finger touches the screen
-
-  private void setEffectsStage2()
-    {
-    resetDynamics1(2);
-    resetDynamics3(2);
-
-    Static3D point3h = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D scaleS  = new Static3D(s001,s001,s001);
-    Static3D scaleF  = new Static3D(s014,s014,s014);
-    Static3D pointH  = new Static3D(s033,s033,s033);
-
-    mPosition1.set(scaleS);
-    mDyn1.add(scaleS);
-    mDyn1.add(scaleF);
-
-    mDynMoveHand1.add(point3h);
-    mDynMoveShad1.add(point3h);
-    mDynScaleHand1.add(pointH);
-    mDynScaleShad1.add(mPosition1);
-
-    mNodes[0].changeInputSurface(mTextureCirc);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// first finger moves across the screen in a triangular fashion
-
-  private void setEffectsStage3()
-    {
-    resetDynamics1(3);
-    resetDynamics3(3);
-
-    Static3D scaleS = new Static3D(s014,s014,s014);
-    Static3D pointH = new Static3D(s033,s033,s033);
-    Static3D point1 = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D point2 = new Static3D(     +D,-Y1  +D, 0);
-    Static3D point3 = new Static3D(+X2  +D,-Y1  +D, 0);
-    Static3D point4 = new Static3D(+X2  +D,     +D, 0);
-    Static3D point5 = new Static3D(+X2  +D,+Y1  +D, 0);
-    Static3D point6 = new Static3D(     +D,     +D, 0);
-
-    mDynScaleHand1.add(pointH);
-    mDynScaleShad1.add(scaleS);
-
-    mDyn1.add(point1);
-    mDyn1.add(point1);
-    mDyn1.add(point2);
-    mDyn1.add(point3);
-    mDyn1.add(point3);
-    mDyn1.add(point4);
-    mDyn1.add(point5);
-    mDyn1.add(point5);
-    mDyn1.add(point6);
-    mDyn1.add(point1);
-    mDyn1.add(point1);
-
-    mPosition1.set(point1);
-
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition1);
-
-    RubikSurfaceView view = mControl.getSurfaceView();
-    float x = point1.get0() + mWidth*0.5f;
-    float y = mHeight*0.5f - point1.get1();
-
-    /*
-    view.prepareDown();
-    view.actionDown(x,y);
-
-     */
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// first finger un-touches the screen
-
-  private void setEffectsStage4()
-    {
-    resetDynamics1(4);
-    resetDynamics3(4);
-
-    Static3D point3h = new Static3D(-X2+D,-Y1+D, 0);
-    Static3D scaleS  = new Static3D(s014,s014,s014);
-    Static3D scaleF  = new Static3D(s001,s001,s001);
-    Static3D pointH  = new Static3D(s033,s033,s033);
-
-    mDyn1.add(scaleS);
-    mDyn1.add(scaleF);
-    mPosition1.set(scaleS);
-
-    mDynMoveHand1.add(point3h);
-    mDynMoveShad1.add(point3h);
-    mDynScaleHand1.add(pointH);
-    mDynScaleShad1.add(mPosition1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// first finger un-touches the screen (part2)
-
-  private void setEffectsStage5()
-    {
-    resetDynamics1(5);
-    resetDynamics3(5);
-
-    Static3D pointH = new Static3D(-X2    ,-Y1    , 0);
-    Static3D point0 = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D pointS = new Static3D(-X2+2*D,-Y1+2*D, 0);
-    Static3D pointSc = new Static3D(s033,s033,s033);
-
-    mPosition1.set(point0);
-    mPosition2.set(point0);
-
-    mDyn1.add(point0);
-    mDyn1.add(pointH);
-    mDyn2.add(point0);
-    mDyn2.add(pointS);
-
-    mDynScaleHand1.add(pointSc);
-    mDynScaleShad1.add(pointSc);
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition2);
-
-    mNodes[0].changeInputSurface(mTextureShad);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// second finger appears
-
-  private void setEffectsStage6()
-    {
-    resetDynamics1(6);
-    resetDynamics2(6);
-    resetDynamics3(6);
-
-    Static3D pointH = new Static3D(-X2    ,-Y1    , 0);
-    Static3D pointS = new Static3D(-X2+2*D,-Y1+2*D, 0);
-    Static3D pointSc= new Static3D(s033,s033,s033);
-
-    mDynScaleHand1.add(pointSc);
-    mDynScaleShad1.add(pointSc);
-    mDynMoveHand1.add(pointH);
-    mDynMoveShad1.add(pointS);
-
-    Static3D point0h = new Static3D( X0    , Y1    , 0);
-    Static3D point1h = new Static3D( X1    , Y1    , 0);
-    Static3D point2h = new Static3D( X2    , Y1    , 0);
-    Static3D point0s = new Static3D( X0+2*D, Y1+2*D, 0);
-    Static3D point1s = new Static3D( X1+2*D, Y1+2*D, 0);
-    Static3D point2s = new Static3D( X2+2*D, Y1+2*D, 0);
-    Static3D pointSm = new Static3D(-s033,s033,s033);
-
-    mPosition1.set(point0h);
-    mPosition2.set(point0s);
-
-    mDyn1.add(point0h);
-    mDyn1.add(point1h);
-    mDyn1.add(point2h);
-    mDyn1.add(point2h);
-    mDyn2.add(point0s);
-    mDyn2.add(point1s);
-    mDyn2.add(point2s);
-    mDyn2.add(point2s);
-
-    mDynScaleHand2.add(pointSm);
-    mDynScaleShad2.add(pointSm);
-    mDynMoveHand2.add(mPosition1);
-    mDynMoveShad2.add(mPosition2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers touch the screen (part1)
-
-  private void setEffectsStage7()
-    {
-    resetDynamics1(7);
-    resetDynamics2(7);
-    resetDynamics3(7);
-    resetDynamics4(7);
-
-    Static3D point1Sc= new Static3D( s033,s033,s033);
-    Static3D point2Sc= new Static3D(-s033,s033,s033);
-    mDynScaleHand1.add(point1Sc);
-    mDynScaleShad1.add(point1Sc);
-    mDynScaleHand2.add(point2Sc);
-    mDynScaleShad2.add(point2Sc);
-
-    Static3D point1H = new Static3D(-X2    ,-Y1    , 0);
-    Static3D point1F = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D point1S = new Static3D(-X2+2*D,-Y1+2*D, 0);
-
-    mDyn1.add(point1H);
-    mDyn1.add(point1F);
-    mDyn2.add(point1S);
-    mDyn2.add(point1F);
-
-    mPosition1.set(point1H);
-    mPosition2.set(point1S);
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition2);
-
-    Static3D point2H = new Static3D( X2    , Y1    , 0);
-    Static3D point2F = new Static3D( X2  +D, Y1  +D, 0);
-    Static3D point2S = new Static3D( X2+2*D, Y1+2*D, 0);
-
-    mDyn3.add(point2H);
-    mDyn3.add(point2F);
-    mDyn4.add(point2S);
-    mDyn4.add(point2F);
-
-    mPosition3.set(point2H);
-    mPosition4.set(point2S);
-    mDynMoveHand2.add(mPosition3);
-    mDynMoveShad2.add(mPosition4);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers touch the screen (part2)
-
-  private void setEffectsStage8()
-    {
-    resetDynamics1(8);
-    resetDynamics2(8);
-    resetDynamics3(8);
-
-    Static3D point1h= new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D point2h= new Static3D( X2  +D, Y1  +D, 0);
-    Static3D scale1S = new Static3D( s001,s001,s001);
-    Static3D scale1F = new Static3D( s014,s014,s014);
-    Static3D point1H = new Static3D( s033,s033,s033);
-    Static3D scale2S = new Static3D(-s001,s001,s001);
-    Static3D scale2F = new Static3D(-s014,s014,s014);
-    Static3D point2H = new Static3D(-s033,s033,s033);
-
-    mPosition1.set(scale1S);
-    mDyn1.add(scale1S);
-    mDyn1.add(scale1F);
-
-    mDynMoveHand1.add(point1h);
-    mDynMoveShad1.add(point1h);
-    mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(mPosition1);
-
-    mPosition2.set(scale2S);
-    mDyn2.add(scale2S);
-    mDyn2.add(scale2F);
-
-    mDynMoveHand2.add(point2h);
-    mDynMoveShad2.add(point2h);
-    mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(mPosition2);
-
-    mNodes[0].changeInputSurface(mTextureCirc);
-    mNodes[1].changeInputSurface(mTextureCirc);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers approach each other
-
-  private void setEffectsStage9()
-    {
-    resetDynamics1(9);
-    resetDynamics2(9);
-    resetDynamics3(9);
-
-    Static3D point1s = new Static3D(-X2+D,-Y1+D, 0);
-    Static3D point2s = new Static3D( X2+D, Y1+D, 0);
-    Static3D point1f = new Static3D(-Y1*F,-Y1*F, 0);
-    Static3D point2f = new Static3D( Y1*F, Y1*F, 0);
-    Static3D scale1F = new Static3D( s014,s014,s014);
-    Static3D point1H = new Static3D( s033,s033,s033);
-    Static3D scale2F = new Static3D(-s014,s014,s014);
-    Static3D point2H = new Static3D(-s033,s033,s033);
-
-    mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(scale1F);
-    mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(scale2F);
-
-    mDyn1.add(point1s);
-    mDyn1.add(point1f);
-    mDyn2.add(point2s);
-    mDyn2.add(point2f);
-
-    mPosition1.set(point1s);
-    mPosition2.set(point2s);
-
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition1);
-    mDynMoveHand2.add(mPosition2);
-    mDynMoveShad2.add(mPosition2);
-
-    float x1 = point1s.get0() + mWidth*0.5f;
-    float y1 = mHeight*0.5f - point1s.get1();
-    float x2 = point2s.get0() + mWidth*0.5f;
-    float y2 = mHeight*0.5f - point2s.get1();
-
-    RubikSurfaceView view = mControl.getSurfaceView();
-
-    /*
-    view.prepareDown();
-    view.prepareDown2();
-    view.actionDown(x1,y1);
-    view.actionDown2(x1,y1,x2,y2);
-
-     */
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers go around
-
-  private void setEffectsStage10()
-    {
-    resetDynamics1(10);
-    resetDynamics2(10);
-    resetDynamics3(10);
-
-    Static3D scale1F = new Static3D( s014,s014,s014);
-    Static3D point1H = new Static3D( s033,s033,s033);
-    Static3D scale2F = new Static3D(-s014,s014,s014);
-    Static3D point2H = new Static3D(-s033,s033,s033);
-
-    Static3D point0= new Static3D(-Y1*F,-Y1*F, 0);
-    Static3D point1= new Static3D(-Y1*F, Y1*F, 0);
-    Static3D point2= new Static3D( Y1*F, Y1*F, 0);
-    Static3D point3= new Static3D( Y1*F,-Y1*F, 0);
-
-    mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(scale1F);
-    mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(scale2F);
-
-    mDyn1.add(point0);
-    mDyn1.add(point1);
-    mDyn1.add(point2);
-    mDyn1.add(point3);
-    mDyn1.add(point0);
-
-    mDyn2.add(point2);
-    mDyn2.add(point3);
-    mDyn2.add(point0);
-    mDyn2.add(point1);
-    mDyn2.add(point2);
-
-    mDyn1.setConvexity(1.0f);
-    mDyn2.setConvexity(1.0f);
-
-    mDyn1.setSpeedMode(Dynamic.SPEED_MODE_SEGMENT_CONSTANT);
-    mDyn2.setSpeedMode(Dynamic.SPEED_MODE_SEGMENT_CONSTANT);
-
-    mPosition1.set(point0);
-    mPosition2.set(point2);
-
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition1);
-    mDynMoveHand2.add(mPosition2);
-    mDynMoveShad2.add(mPosition2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers move away from each other
-
-  private void setEffectsStage11()
-    {
-    resetDynamics1(11);
-    resetDynamics2(11);
-    resetDynamics3(11);
-
-    Static3D point1s= new Static3D(-X2+D,-Y1+D, 0);
-    Static3D point2s= new Static3D( X2+D, Y1+D, 0);
-    Static3D point1f= new Static3D(-Y1*F,-Y1*F, 0);
-    Static3D point2f= new Static3D( Y1*F, Y1*F, 0);
-    Static3D scale1F= new Static3D( s014,s014,s014);
-    Static3D point1H= new Static3D( s033,s033,s033);
-    Static3D scale2F= new Static3D(-s014,s014,s014);
-    Static3D point2H= new Static3D(-s033,s033,s033);
-
-    mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(scale1F);
-    mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(scale2F);
-
-    mDyn1.add(point1f);
-    mDyn1.add(point1s);
-    mDyn2.add(point2f);
-    mDyn2.add(point2s);
-
-    mDyn1.setConvexity(0.0f);
-    mDyn2.setConvexity(0.0f);
-
-    mDyn1.setSpeedMode(Dynamic.SPEED_MODE_SMOOTH);
-    mDyn2.setSpeedMode(Dynamic.SPEED_MODE_SMOOTH);
-
-    mPosition1.set(point1f);
-    mPosition2.set(point2f);
-
-    mDynMoveHand1.add(mPosition1);
-    mDynMoveShad1.add(mPosition1);
-    mDynMoveHand2.add(mPosition2);
-    mDynMoveShad2.add(mPosition2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers un-touch the screen (part1)
-
-  private void setEffectsStage12()
-    {
-    resetDynamics1(12);
-    resetDynamics2(12);
-    resetDynamics3(12);
-
-    Static3D point1h = new Static3D(-X2+D,-Y1+D, 0);
-    Static3D point2h = new Static3D( X2+D, Y1+D, 0);
-    Static3D scale1S = new Static3D( s014,s014,s014);
-    Static3D scale1F = new Static3D( s001,s001,s001);
-    Static3D point1H = new Static3D( s033,s033,s033);
-    Static3D scale2S = new Static3D(-s014,s014,s014);
-    Static3D scale2F = new Static3D(-s001,s001,s001);
-    Static3D point2H = new Static3D(-s033,s033,s033);
-
-    mPosition1.set(scale1S);
-    mPosition2.set(scale2S);
-
-    mDyn1.add(scale1S);
-    mDyn1.add(scale1F);
-    mDyn2.add(scale2S);
-    mDyn2.add(scale2F);
-
-    mDynMoveHand1.add(point1h);
-    mDynMoveShad1.add(point1h);
-    mDynScaleHand1.add(point1H);
-    mDynScaleShad1.add(mPosition1);
-
-    mDynMoveHand2.add(point2h);
-    mDynMoveShad2.add(point2h);
-    mDynScaleHand2.add(point2H);
-    mDynScaleShad2.add(mPosition2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// both fingers un-touch the screen (part2)
-
-  private void setEffectsStage13()
-    {
-    resetDynamics1(13);
-    resetDynamics2(13);
-    resetDynamics3(13);
-    resetDynamics4(13);
-
-    Static3D point1_0 = new Static3D(-X2  +D,-Y1  +D, 0);
-    Static3D point11H = new Static3D(-X2    ,-Y1    , 0);
-    Static3D point12H = new Static3D(-X1    ,-Y1    , 0);
-    Static3D point13H = new Static3D(-X0    ,-Y1    , 0);
-    Static3D point11S = new Static3D(-X2+2*D,-Y1+2*D, 0);
-    Static3D point12S = new Static3D(-X1+2*D,-Y1+2*D, 0);
-    Static3D point13S = new Static3D(-X0+2*D,-Y1+2*D, 0);
-    Static3D point1Sc = new Static3D( s033,s033,s033);
-
-    mPosition1.set(point1_0);
-    mDynMoveHand1.add(mPosition1);
-    mPosition2.set(point1_0);
-    mDynMoveShad1.add(mPosition2);
-
-    mDynScaleHand1.add(point1Sc);
-    mDynScaleShad1.add(point1Sc);
-
-    mDyn1.add(point1_0);
-    mDyn1.add(point11H);
-    mDyn1.add(point11H);
-    mDyn1.add(point12H);
-    mDyn1.add(point13H);
-
-    mDyn2.add(point1_0);
-    mDyn2.add(point11S);
-    mDyn2.add(point11S);
-    mDyn2.add(point12S);
-    mDyn2.add(point13S);
-
-    Static3D point2_0 = new Static3D( X2  +D, Y1  +D, 0);
-    Static3D point21H = new Static3D( X2    , Y1    , 0);
-    Static3D point22H = new Static3D( X1    , Y1    , 0);
-    Static3D point23H = new Static3D( X0    , Y1    , 0);
-    Static3D point21S = new Static3D( X2+2*D, Y1+2*D, 0);
-    Static3D point22S = new Static3D( X1+2*D, Y1+2*D, 0);
-    Static3D point23S = new Static3D( X0+2*D, Y1+2*D, 0);
-    Static3D point2Sc= new Static3D(-s033,s033,s033);
-
-    mPosition3.set(point2_0);
-    mDynMoveHand2.add(mPosition3);
-    mPosition4.set(point2_0);
-    mDynMoveShad2.add(mPosition4);
-
-    mDynScaleHand2.add(point2Sc);
-    mDynScaleShad2.add(point2Sc);
-
-    mDyn3.add(point2_0);
-    mDyn3.add(point21H);
-    mDyn3.add(point21H);
-    mDyn3.add(point22H);
-    mDyn3.add(point23H);
-
-    mDyn4.add(point2_0);
-    mDyn4.add(point21S);
-    mDyn4.add(point21S);
-    mDyn4.add(point22S);
-    mDyn4.add(point23S);
-
-    mNodes[0].changeInputSurface(mTextureShad);
-    mNodes[1].changeInputSurface(mTextureShad);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createEffects()
-    {
-    mEffects = new DistortedEffects[NUM_EFFE];
-    for(int i=0; i<NUM_EFFE; i++) mEffects[i]= new DistortedEffects();
-
-    int time = DUR[0];
-
-    mDyn1 = new Dynamic3D(time,0.5f);
-    mDyn1.setMode(Dynamic.MODE_PATH);
-    mDyn1.setConvexity(0.0f);
-    mDyn2 = new Dynamic3D(time,0.5f);
-    mDyn2.setMode(Dynamic.MODE_PATH);
-    mDyn2.setConvexity(0.0f);
-    mDyn3 = new Dynamic3D(time,0.5f);
-    mDyn3.setMode(Dynamic.MODE_PATH);
-    mDyn3.setConvexity(0.0f);
-    mDyn4 = new Dynamic3D(time,0.5f);
-    mDyn4.setMode(Dynamic.MODE_PATH);
-    mDyn4.setConvexity(0.0f);
-
-    mPosition1 = new Static3D(0,0,0);
-    mPosition2 = new Static3D(0,0,0);
-    mPosition3 = new Static3D(0,0,0);
-    mPosition4 = new Static3D(0,0,0);
-
-    tmpBuffer = new float[12];
-
-    mDynMoveHand1 = new Dynamic3D(time,0.5f);
-    mDynMoveHand1.setMode(Dynamic.MODE_PATH);
-    mDynMoveHand1.setConvexity(0.0f);
-    mDynMoveShad1 = new Dynamic3D(time,0.5f);
-    mDynMoveShad1.setMode(Dynamic.MODE_PATH);
-    mDynMoveShad1.setConvexity(0.0f);
-    mDynScaleHand1 = new Dynamic3D(time,0.5f);
-    mDynScaleHand1.setMode(Dynamic.MODE_PATH);
-    mDynScaleHand1.setConvexity(0.0f);
-    mDynScaleShad1 = new Dynamic3D(time,0.5f);
-    mDynScaleShad1.setMode(Dynamic.MODE_PATH);
-    mDynScaleShad1.setConvexity(0.0f);
-
-    MatrixEffectMove  moveHand1 = new MatrixEffectMove(mDynMoveHand1);
-    MatrixEffectMove  moveShad1 = new MatrixEffectMove(mDynMoveShad1);
-    MatrixEffectScale scaleHand1= new MatrixEffectScale(mDynScaleHand1);
-    MatrixEffectScale scaleShad1= new MatrixEffectScale(mDynScaleShad1);
-
-    mEffects[0].apply(scaleShad1);
-    mEffects[0].apply(moveShad1);
-    mEffects[2].apply(scaleHand1);
-    mEffects[2].apply(moveHand1);
-
-    mDynMoveHand2 = new Dynamic3D(time,0.5f);
-    mDynMoveHand2.setMode(Dynamic.MODE_PATH);
-    mDynMoveHand2.setConvexity(0.0f);
-    mDynMoveShad2 = new Dynamic3D(time,0.5f);
-    mDynMoveShad2.setMode(Dynamic.MODE_PATH);
-    mDynMoveShad2.setConvexity(0.0f);
-    mDynScaleHand2 = new Dynamic3D(time,0.5f);
-    mDynScaleHand2.setMode(Dynamic.MODE_PATH);
-    mDynScaleHand2.setConvexity(0.0f);
-    mDynScaleShad2 = new Dynamic3D(time,0.5f);
-    mDynScaleShad2.setMode(Dynamic.MODE_PATH);
-    mDynScaleShad2.setConvexity(0.0f);
-
-    MatrixEffectMove  moveHand2 = new MatrixEffectMove(mDynMoveHand2);
-    MatrixEffectMove  moveShad2 = new MatrixEffectMove(mDynMoveShad2);
-    MatrixEffectScale scaleHand2= new MatrixEffectScale(mDynScaleHand2);
-    MatrixEffectScale scaleShad2= new MatrixEffectScale(mDynScaleShad2);
-
-    mEffects[1].apply(scaleShad2);
-    mEffects[1].apply(moveShad2);
-    mEffects[3].apply(scaleHand2);
-    mEffects[3].apply(moveHand2);
-
-    DistortedScreen screen = mControl.getScreen();
-    mWidth = screen.getWidth();
-    mHeight= screen.getHeight();
-
-    X0   = mWidth*0.65f;
-    X1   = mWidth*0.50f;
-    X2   = mWidth*0.35f;
-    Y1   = mHeight*0.28f;
-    D    = mWidth*0.01f;
-    s001 = mWidth*0.0001f;
-    s014 = mWidth*0.14f;
-    s033 = mWidth*0.33f;
-    F    = 0.50f;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createNodes()
-    {
-    if( mNodes==null )
-      {
-      mNodes = new DistortedNode[NUM_NODE];
-      mQuad  = new MeshQuad();
-      }
-
-    RubikActivity act = mControl.getActivity();
-
-    if( act!=null )
-      {
-      Bitmap bmpCirc = openBitmap(act, R.drawable.ui_fading_circle);
-      Bitmap bmpShad = openBitmap(act, R.drawable.ui_hand_shadow);
-      Bitmap bmpHand = openBitmap(act, R.drawable.ui_hand_pointer);
-
-      mTextureCirc = new DistortedTexture();
-      mTextureShad = new DistortedTexture();
-      DistortedTexture textureHand = new DistortedTexture();
-
-      if( bmpCirc!=null ) mTextureCirc.setTexture(bmpCirc);
-      if( bmpShad!=null ) mTextureShad.setTexture(bmpShad);
-      if( bmpHand!=null ) textureHand.setTexture(bmpHand);
-
-      mNodes[0]= new DistortedNode(mTextureShad,mEffects[0],mQuad);
-      mNodes[1]= new DistortedNode(mTextureShad,mEffects[1],mQuad);
-      mNodes[2]= new DistortedNode( textureHand,mEffects[2],mQuad);
-      mNodes[3]= new DistortedNode( textureHand,mEffects[3],mQuad);
-      }
-    else
-      {
-      android.util.Log.e("D", "Activity NULL!!");
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  long getEffectID()
-    {
-    return mEffectID;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode[] getNodes()
-    {
-    if( mEffects==null ) createEffects();
-    createNodes();
-    setEffectsStage1();
-
-    return mNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode[] returnNodes()
-    {
-    return mNodes;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  RubikControlWhole(RubikControl control)
-    {
-    mControl = control;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void stageFinished(int stage)
-    {
-    switch(stage)
-      {
-      case  1: setEffectsStage2() ; break;
-      case  2: setEffectsStage3() ; break;
-      case  3: setEffectsStage4() ; break;
-      case  4: setEffectsStage5() ; break;
-      case  5: setEffectsStage6() ; break;
-      case  6: setEffectsStage7() ; break;
-      case  7: setEffectsStage8() ; break;
-      case  8: setEffectsStage9() ; break;
-      case  9: setEffectsStage10(); break;
-      case 10: setEffectsStage11(); break;
-      case 11: setEffectsStage12(); break;
-      case 12: setEffectsStage13(); break;
-      default: setPostFrame(false);
-               mEffectID = -1;
-               mControl.effectFinished(mEffectID);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC
-
-  public void postDrawFrame(long time)
-    {
-    if( mLastTime<0 ) mLastTime = time;
-    else mDiffTime = time - mLastTime;
-    mLastTime = time;
-
-    switch(DYN[mCurrentStage-1])
-      {
-      case 1: boolean finished1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
-              mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
-
-              if( mCurrentStage==3 )
-                {
-                RubikSurfaceView view = mControl.getSurfaceView();
-                float x1 = tmpBuffer[0]+mWidth*0.5f;
-                float y1 = mHeight*0.5f-tmpBuffer[1];
-                //view.actionMove(x1,y1,0,0);
-                }
-
-              if( finished1 )
-                {
-                if( mCurrentStage==3 )
-                  {
-                  RubikSurfaceView view = mControl.getSurfaceView();
-                  //view.prepareUp();
-                  //view.actionUp();
-                  }
-
-                stageFinished(mCurrentStage);
-                }
-              break;
-      case 2: boolean finished2_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
-              boolean finished2_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
-              mPosition1.set(tmpBuffer[0], tmpBuffer[1], tmpBuffer[2]);
-              mPosition2.set(tmpBuffer[3], tmpBuffer[4], tmpBuffer[5]);
-
-              if( mCurrentStage==9 || mCurrentStage==10 || mCurrentStage==11 )
-                {
-                RubikSurfaceView view = mControl.getSurfaceView();
-                float x1 = tmpBuffer[0]+mWidth*0.5f;
-                float y1 = mHeight*0.5f-tmpBuffer[1];
-                float x2 = tmpBuffer[3]+mWidth*0.5f;
-                float y2 = mHeight*0.5f-tmpBuffer[4];
-                //view.prepareMove(x1,y1,x2,y2);
-                //view.actionMove(x1,y1,x2,y2);
-                }
-
-              if( finished2_1 && finished2_2 )
-                {
-                 if( mCurrentStage==11 )
-                  {
-                  RubikSurfaceView view = mControl.getSurfaceView();
-                  //view.prepareUp();
-                  //view.actionUp2(true,0,0,false,0,0);
-                  //view.actionUp();
-                  }
-
-                stageFinished(mCurrentStage);
-                }
-              break;
-      case 4: boolean finished4_1 = mDyn1.get( tmpBuffer,0, time, mDiffTime);
-              boolean finished4_2 = mDyn2.get( tmpBuffer,3, time, mDiffTime);
-              boolean finished4_3 = mDyn3.get( tmpBuffer,6, time, mDiffTime);
-              boolean finished4_4 = mDyn4.get( tmpBuffer,9, time, mDiffTime);
-              mPosition1.set(tmpBuffer[0], tmpBuffer[ 1], tmpBuffer[ 2]);
-              mPosition2.set(tmpBuffer[3], tmpBuffer[ 4], tmpBuffer[ 5]);
-              mPosition3.set(tmpBuffer[6], tmpBuffer[ 7], tmpBuffer[ 8]);
-              mPosition4.set(tmpBuffer[9], tmpBuffer[10], tmpBuffer[11]);
-              if( finished4_1 && finished4_2 && finished4_3 && finished4_4 ) stageFinished(mCurrentStage);
-              break;
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/helpers/LockController.java b/src/main/java/org/distorted/helpers/LockController.java
index f7064783..ab8dd420 100644
--- a/src/main/java/org/distorted/helpers/LockController.java
+++ b/src/main/java/org/distorted/helpers/LockController.java
@@ -29,7 +29,6 @@ import android.widget.LinearLayout;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
 import org.distorted.objectlib.helpers.TwistyActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -37,7 +36,6 @@ public class LockController
   {
   private static final int LOCK_TIME = 300;
 
-  private ObjectPreRender mPre;
   private ImageButton mLockButton;
   private long mLockTime;
   private Timer mTimer;
diff --git a/src/main/java/org/distorted/helpers/MovesController.java b/src/main/java/org/distorted/helpers/MovesController.java
index 1227ee41..c5dae639 100644
--- a/src/main/java/org/distorted/helpers/MovesController.java
+++ b/src/main/java/org/distorted/helpers/MovesController.java
@@ -28,7 +28,7 @@ import android.widget.LinearLayout;
 import org.distorted.objectlib.helpers.BlockController;
 import org.distorted.objectlib.helpers.MovesFinished;
 import org.distorted.objectlib.helpers.TwistyActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
+import org.distorted.objectlib.main.ObjectControl;
 
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
@@ -53,7 +53,7 @@ public class MovesController implements MovesFinished
 
   private final ArrayList<Move> mMoves;
   private boolean mCanPrevMove;
-  private ObjectPreRender mPre;
+  private ObjectControl mControl;
   private ImageButton mPrevButton;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -104,9 +104,9 @@ public class MovesController implements MovesFinished
         if( angle!=0 )
           {
           mCanPrevMove = false;
-          mPre = act.getPreRender();
-          mPre.blockTouch(BlockController.MOVES_PLACE_0);
-          mPre.addRotation(this, axis, row, -angle, duration);
+          mControl = act.getControl();
+          mControl.blockTouch(BlockController.MOVES_PLACE_0);
+          mControl.addRotation(this, axis, row, -angle, duration);
           }
         else
           {
@@ -146,7 +146,7 @@ public class MovesController implements MovesFinished
   public void onActionFinished(final long effectID)
     {
     mCanPrevMove = true;
-    mPre.unblockTouch();
+    mControl.unblockTouch();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikActivity.java b/src/main/java/org/distorted/main/RubikActivity.java
index 5fc2bd1b..99a9683d 100644
--- a/src/main/java/org/distorted/main/RubikActivity.java
+++ b/src/main/java/org/distorted/main/RubikActivity.java
@@ -19,6 +19,8 @@
 
 package org.distorted.main;
 
+import java.util.Locale;
+
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Build;
@@ -37,25 +39,22 @@ import com.google.firebase.analytics.FirebaseAnalytics;
 
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
-import org.distorted.library.type.Static4D;
 
-import org.distorted.objectlib.main.ObjectPreRender;
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.main.ObjectType;
 import org.distorted.objectlib.helpers.BlockController;
+import org.distorted.objectlib.effects.BaseEffect;
+import org.distorted.objectlib.helpers.TwistyActivity;
 
 import org.distorted.dialogs.RubikDialogError;
 import org.distorted.dialogs.RubikDialogPrivacy;
-import org.distorted.objectlib.effects.BaseEffect;
-import org.distorted.objectlib.helpers.TwistyActivity;
 import org.distorted.network.RubikScores;
 import org.distorted.network.RubikNetwork;
 import org.distorted.screens.ScreenList;
 import org.distorted.screens.RubikScreenPlay;
 import org.distorted.tutorials.TutorialActivity;
 
-import java.util.Locale;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class RubikActivity extends TwistyActivity
@@ -122,7 +121,6 @@ public class RubikActivity extends TwistyActivity
       hideNavigationBar();
       cutoutHack();
       computeBarHeights();
-      // askForPermissions();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -216,26 +214,6 @@ public class RubikActivity extends TwistyActivity
         }
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private void askForPermissions()
-      {
-      final int REQUEST_EXTERNAL_STORAGE = 1;
-
-      String[] PERMISSIONS_STORAGE =
-        {
-        android.Manifest.permission.READ_EXTERNAL_STORAGE,
-        android.Manifest.permission.WRITE_EXTERNAL_STORAGE
-        };
-
-      int permission = androidx.core.app.ActivityCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
-
-      if (permission != android.content.pm.PackageManager.PERMISSION_GRANTED)
-        {
-        androidx.core.app.ActivityCompat.requestPermissions( this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE );
-        }
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
     
     @Override
@@ -274,7 +252,7 @@ public class RubikActivity extends TwistyActivity
 
       RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
       ObjectType object = play.getObject();
-      view.getPreRender().changeObject(object);
+      view.getObjectControl().changeObject(object);
       
       if( mIsChinese && !mPolicyAccepted ) PrivacyPolicy();
       }
@@ -309,7 +287,7 @@ public class RubikActivity extends TwistyActivity
 
       ScreenList.savePreferences(editor);
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.getPreRender().savePreferences(editor);
+      view.getObjectControl().savePreferences(editor);
 
       editor.apply();
       }
@@ -335,7 +313,7 @@ public class RubikActivity extends TwistyActivity
       ScreenList.restorePreferences(preferences);
 
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      view.getPreRender().restorePreferences(preferences);
+      view.getObjectControl().restorePreferences(preferences);
 
       RubikScores scores = RubikScores.getInstance();
 
@@ -390,32 +368,24 @@ public class RubikActivity extends TwistyActivity
     public TwistyObject getObject()
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      ObjectPreRender pre = view.getPreRender();
-      return pre.getObject();
+      return view.getObjectControl().getObject();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public DistortedScreen getScreen()
+    public ObjectControl getControl()
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikRenderer renderer = view.getRenderer();
-      return renderer.getScreen();
+      return view.getObjectControl();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public ObjectPreRender getPreRender()
+    public DistortedScreen getScreen()
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      return view.getPreRender();
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public RubikSurfaceView getSurfaceView()
-      {
-      return findViewById(R.id.rubikSurfaceView);
+      RubikRenderer renderer = view.getRenderer();
+      return renderer.getScreen();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -437,11 +407,11 @@ public class RubikActivity extends TwistyActivity
     public void changeObject(ObjectType newObject, boolean reportChange)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      ObjectPreRender pre = view.getPreRender();
+      ObjectControl control = view.getObjectControl();
 
       if( reportChange )
         {
-        TwistyObject oldObject = pre.getObject();
+        TwistyObject oldObject = control.getObject();
 
         if( oldObject!=null )
           {
@@ -473,7 +443,7 @@ public class RubikActivity extends TwistyActivity
           }
         }
 
-      pre.changeObject(newObject);
+      control.changeObject(newObject);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -495,8 +465,8 @@ public class RubikActivity extends TwistyActivity
     public void setupObject(ObjectType object, int[][] moves)
       {
       RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      ObjectPreRender pre = view.getPreRender();
-      pre.setupObject(object,moves);
+      ObjectControl control = view.getObjectControl();
+      control.setupObject(object,moves);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -534,15 +504,6 @@ public class RubikActivity extends TwistyActivity
         }
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void setControlState(boolean on)
-      {
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      RubikRenderer renderer = view.getRenderer();
-      renderer.setControlState(on);
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public void acceptPrivacy()
@@ -589,8 +550,9 @@ public class RubikActivity extends TwistyActivity
       {
       setLock();
 
-      ObjectPreRender pre = getPreRender();
-      pre.blockEverything(place);
+      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
+      ObjectControl control = view.getObjectControl();
+      control.blockEverything(place);
 
       RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
       play.setLockState(this);
@@ -602,21 +564,14 @@ public class RubikActivity extends TwistyActivity
       {
       unsetLock();
 
-      ObjectPreRender pre = getPreRender();
-      pre.unblockEverything();
+      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
+      ObjectControl control = view.getObjectControl();
+      control.unblockEverything();
 
       RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
       play.setLockState(this);
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public Static4D getCurrQuat()
-      {
-      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      return view.getQuat();
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public void switchTutorial(String url, ObjectType object)
diff --git a/src/main/java/org/distorted/main/RubikRenderer.java b/src/main/java/org/distorted/main/RubikRenderer.java
index d2be6de7..b9634d72 100644
--- a/src/main/java/org/distorted/main/RubikRenderer.java
+++ b/src/main/java/org/distorted/main/RubikRenderer.java
@@ -22,7 +22,6 @@ package org.distorted.main;
 import android.opengl.GLES30;
 import android.opengl.GLSurfaceView;
 
-import org.distorted.control.RubikControl;
 import org.distorted.objectlib.effects.BaseEffect;
 import org.distorted.library.effect.EffectType;
 import org.distorted.library.effect.VertexEffectQuaternion;
@@ -31,6 +30,7 @@ import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.network.RubikNetwork;
+import org.distorted.objectlib.main.ObjectControl;
 
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
@@ -45,10 +45,10 @@ public class RubikRenderer implements GLSurfaceView.Renderer, DistortedLibrary.E
 
    private final RubikSurfaceView mView;
    private final DistortedScreen mScreen;
+   private final ObjectControl mControl;
    private final Fps mFPS;
    private boolean mErrorShown;
    private boolean mDebugSent;
-   private RubikControl mControl;
 
    private static class Fps
      {
@@ -94,18 +94,12 @@ public class RubikRenderer implements GLSurfaceView.Renderer, DistortedLibrary.E
      {
      mErrorShown = false;
      mView = v;
+     mControl = v.getObjectControl();
      mFPS = new Fps();
      mScreen = new DistortedScreen();
      mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   public void setControlState(boolean on)
-     {
-     mControl = on ? RubikControl.getInstance() : null;
-     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // various things are done here delayed, 'after the next render' as not to be done mid-render and
 // cause artifacts.
@@ -115,10 +109,8 @@ public class RubikRenderer implements GLSurfaceView.Renderer, DistortedLibrary.E
      {
      long time = System.currentTimeMillis();
      mFPS.onRender(time);
-     mView.getPreRender().preRender();
+     mControl.preRender();
      mScreen.render(time);
-
-     if( mControl!=null ) mControl.postDrawFrame(time);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -128,7 +120,6 @@ public class RubikRenderer implements GLSurfaceView.Renderer, DistortedLibrary.E
       {
       mScreen.resize(width,height);
       mView.setScreenSize(width,height);
-      mView.getPreRender().setScreenSize(width);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index 890da2d8..454b446a 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -32,7 +32,6 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics;
 import org.distorted.library.type.Static4D;
 
 import org.distorted.objectlib.main.ObjectControl;
-import org.distorted.objectlib.main.ObjectPreRender;
 
 import org.distorted.screens.ScreenList;
 
@@ -69,9 +68,9 @@ public class RubikSurfaceView extends GLSurfaceView
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    ObjectPreRender getPreRender()
+    ObjectControl getObjectControl()
       {
-      return mObjectController.getPreRender();
+      return mObjectController;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -85,8 +84,8 @@ public class RubikSurfaceView extends GLSurfaceView
       if(!isInEditMode())
         {
         RubikActivity act = (RubikActivity)context;
-        mRenderer = new RubikRenderer(this);
         mObjectController = new ObjectControl(act,new RubikObjectStateActioner());
+        mRenderer = new RubikRenderer(this);
 
         final ActivityManager activityManager= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
diff --git a/src/main/java/org/distorted/patterns/RubikPattern.java b/src/main/java/org/distorted/patterns/RubikPattern.java
index 76dcbcb4..fd000cfc 100644
--- a/src/main/java/org/distorted/patterns/RubikPattern.java
+++ b/src/main/java/org/distorted/patterns/RubikPattern.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.distorted.objectlib.helpers.MovesFinished;
-import org.distorted.objectlib.main.ObjectPreRender;
+import org.distorted.objectlib.main.ObjectControl;
 
 import static org.distorted.patterns.RubikPatternList.NUM_OBJECTS;
 
@@ -151,27 +151,27 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void makeMove(ObjectPreRender pre, int pattern)
+    void makeMove(ObjectControl control, int pattern)
       {
       if( !mInitialized ) initialize();
 
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = mPatterns.get(pattern);
-        if( p!=null ) p.makeMove(pre);
+        if( p!=null ) p.makeMove(control);
         }
       }
 
   /////////////////////////////////////////////////////////////
 
-    void backMove(ObjectPreRender pre, int pattern)
+    void backMove(ObjectControl control, int pattern)
       {
       if( !mInitialized ) initialize();
 
       if( pattern>=0 && pattern<numPatterns )
         {
         Pattern p = mPatterns.get(pattern);
-        if( p!=null ) p.backMove(pre);
+        if( p!=null ) p.backMove(control);
         }
       }
 
@@ -317,7 +317,7 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void makeMove(ObjectPreRender pre)
+    void makeMove(ObjectControl control)
       {
       if( !mInitialized ) initialize();
 
@@ -328,21 +328,21 @@ public class RubikPattern
         if( curMove>numMove )
           {
           curMove= 0;
-          pre.initializeObject(null);
+          control.initializeObject(null);
           }
         else
           {
           int axis      = moves[curMove-1][0];
 		      int rowBitmap = moves[curMove-1][1];
 		      int bareAngle = moves[curMove-1][2];
-		      int basicAngle= pre.getObject().getBasicAngle()[axis];
+		      int basicAngle= control.getObject().getBasicAngle()[axis];
           int angle     = bareAngle*(360/basicAngle);
           int duration  = Math.abs(angle)*MILLIS_PER_DEGREE;
 
           if( angle!=0 )
             {
             mCanRotate = false;
-            pre.addRotation(this, axis, rowBitmap, angle, duration);
+            control.addRotation(this, axis, rowBitmap, angle, duration);
             }
           else
             {
@@ -358,7 +358,7 @@ public class RubikPattern
 
   /////////////////////////////////////////////////////////////
 
-    void backMove(ObjectPreRender pre)
+    void backMove(ObjectControl control)
       {
       if( !mInitialized ) initialize();
 
@@ -369,21 +369,21 @@ public class RubikPattern
         if( curMove<0 )
           {
           curMove=numMove;
-          pre.initializeObject(moves);
+          control.initializeObject(moves);
           }
         else
           {
           int axis      = moves[curMove][0];
 		      int rowBitmap = moves[curMove][1];
 		      int bareAngle = moves[curMove][2];
-		      int basicAngle= pre.getObject().getBasicAngle()[axis];
+		      int basicAngle= control.getObject().getBasicAngle()[axis];
           int angle     = bareAngle*(360/basicAngle);
           int duration  = Math.abs(angle)*MILLIS_PER_DEGREE;
 
           if( angle!=0 )
             {
             mCanRotate = false;
-            pre.addRotation(this, axis, rowBitmap, -angle, duration);
+            control.addRotation(this, axis, rowBitmap, -angle, duration);
             }
           else
             {
@@ -576,18 +576,18 @@ public class RubikPattern
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void makeMove(ObjectPreRender pre, int tab, int cat, int pat)
+  public void makeMove(ObjectControl control, int tab, int cat, int pat)
     {
     Category c = getCategory(tab,cat);
-    if( c!=null ) c.makeMove(pre,pat);
+    if( c!=null ) c.makeMove(control,pat);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void backMove(ObjectPreRender pre, int tab, int cat, int pat)
+  public void backMove(ObjectControl control, int tab, int cat, int pat)
     {
     Category c = getCategory(tab,cat);
-    if( c!=null ) c.backMove(pre,pat);
+    if( c!=null ) c.backMove(control,pat);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/screens/RubikScreenPattern.java b/src/main/java/org/distorted/screens/RubikScreenPattern.java
index 2d88a2c0..31447e4a 100644
--- a/src/main/java/org/distorted/screens/RubikScreenPattern.java
+++ b/src/main/java/org/distorted/screens/RubikScreenPattern.java
@@ -30,13 +30,13 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.ObjectType;
 
 import org.distorted.main.R;
 import org.distorted.dialogs.RubikDialogPattern;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.RubikActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
 import org.distorted.patterns.RubikPattern;
 import org.distorted.patterns.RubikPatternList;
 
@@ -177,8 +177,8 @@ public class RubikScreenPattern extends RubikScreenAbstract
       public void onClick(View v)
         {
         RubikPattern pattern = RubikPattern.getInstance();
-        ObjectPreRender pre = act.getPreRender();
-        pattern.backMove( pre, mPatternOrdinal, mCategory, mPattern);
+        ObjectControl control = act.getControl();
+        pattern.backMove( control, mPatternOrdinal, mCategory, mPattern);
         int currMove = pattern.getCurMove(mPatternOrdinal, mCategory, mPattern);
         mMovesText.setText(act.getString(R.string.mo_placeholder,currMove,mNumMoves));
         }
@@ -198,8 +198,8 @@ public class RubikScreenPattern extends RubikScreenAbstract
       public void onClick(View v)
         {
         RubikPattern pattern = RubikPattern.getInstance();
-        ObjectPreRender pre = act.getPreRender();
-        pattern.makeMove( pre, mPatternOrdinal, mCategory, mPattern);
+        ObjectControl control = act.getControl();
+        pattern.makeMove( control, 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/screens/RubikScreenPlay.java b/src/main/java/org/distorted/screens/RubikScreenPlay.java
index 65b75e85..e5920c63 100644
--- a/src/main/java/org/distorted/screens/RubikScreenPlay.java
+++ b/src/main/java/org/distorted/screens/RubikScreenPlay.java
@@ -35,11 +35,11 @@ import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 import android.widget.ScrollView;
 
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.ObjectType;
 
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
 import org.distorted.dialogs.RubikDialogAbout;
 import org.distorted.dialogs.RubikDialogPattern;
 import org.distorted.dialogs.RubikDialogScores;
@@ -59,7 +59,6 @@ public class RubikScreenPlay extends RubikScreenBase
 
   private static final int[] BUTTON_LABELS = { R.string.scores,
                                                R.string.patterns,
-                                            //   R.string.control,
                                                R.string.solver,
                                                R.string.tutorials,
                                                R.string.about };
@@ -149,7 +148,7 @@ public class RubikScreenPlay extends RubikScreenBase
       @Override
       public void onClick(View view)
         {
-        if( mObjectPopup!=null && act.getPreRender().isUINotBlocked())
+        if( mObjectPopup!=null && act.getControl().isUINotBlocked())
           {
           int rowCount = Math.min(mMaxRowCount,mRowCount);
           View popupView = mObjectPopup.getContentView();
@@ -174,7 +173,7 @@ public class RubikScreenPlay extends RubikScreenBase
       @Override
       public void onClick(View view)
         {
-        if( mPlayPopup!=null && act.getPreRender().isUINotBlocked())
+        if( mPlayPopup!=null && act.getControl().isUINotBlocked())
           {
           View popupView = mPlayPopup.getContentView();
           popupView.setSystemUiVisibility(RubikActivity.FLAGS);
@@ -202,7 +201,7 @@ public class RubikScreenPlay extends RubikScreenBase
       @Override
       public void onClick(View view)
         {
-        if( mMenuPopup!=null && act.getPreRender().isUINotBlocked())
+        if( mMenuPopup!=null && act.getControl().isUINotBlocked())
           {
           View popupView = mMenuPopup.getContentView();
           popupView.setSystemUiVisibility(RubikActivity.FLAGS);
@@ -271,7 +270,7 @@ public class RubikScreenPlay extends RubikScreenBase
         @Override
         public void onClick(View v)
           {
-          if( act.getPreRender().isUINotBlocked() && ScreenList.getCurrentScreen()== ScreenList.PLAY )
+          if( act.getControl().isUINotBlocked() && ScreenList.getCurrentScreen()== ScreenList.PLAY )
             {
             mObject = ObjectType.getObject(obj);
             act.changeObject(list, true);
@@ -373,12 +372,6 @@ public class RubikScreenPlay extends RubikScreenBase
               pDiag.setArguments(pBundle);
               pDiag.show( act.getSupportFragmentManager(), RubikDialogPattern.getDialogTag() );
               break;
-/*
-      case 2: RubikControl control = RubikControl.getInstance();
-              //control.animateAll(act);
-              control.animateRotate(act);
-              break;
- */
       case 2: ScreenList.switchScreen(act, ScreenList.SVER);
               break;
       case 3: RubikDialogTutorial tDiag = new RubikDialogTutorial();
@@ -406,7 +399,7 @@ public class RubikScreenPlay extends RubikScreenBase
       @Override
       public void onClick(View v)
         {
-        act.getPreRender().solveObject();
+        act.getControl().solveObject();
         mMovesController.clearMoves(act);
         }
       });
@@ -557,13 +550,13 @@ public class RubikScreenPlay extends RubikScreenBase
         @Override
         public void onClick(View v)
           {
-          ObjectPreRender pre = act.getPreRender();
+          ObjectControl control = act.getControl();
 
-          if(pre.isUINotBlocked())
+          if(control.isUINotBlocked())
             {
             if( mPlayPopup!=null ) mPlayPopup.dismiss();
             mLevelValue = level;
-            pre.scrambleObject(scrambles);
+            control.scrambleObject(scrambles);
             }
           }
         });
diff --git a/src/main/java/org/distorted/screens/RubikScreenSolution.java b/src/main/java/org/distorted/screens/RubikScreenSolution.java
index f4df8729..e8c38eb7 100644
--- a/src/main/java/org/distorted/screens/RubikScreenSolution.java
+++ b/src/main/java/org/distorted/screens/RubikScreenSolution.java
@@ -28,13 +28,13 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
-
 import org.distorted.objectlib.helpers.MovesFinished;
+
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
 import org.distorted.patterns.RubikPattern;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -121,8 +121,8 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
       @Override
       public void onClick(View v)
         {
-        ObjectPreRender pre = act.getPreRender();
-        backMove(pre);
+        ObjectControl control = act.getControl();
+        backMove(control);
         mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
         }
       });
@@ -140,8 +140,8 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
       @Override
       public void onClick(View v)
         {
-        ObjectPreRender pre = act.getPreRender();
-        makeMove(pre);
+        ObjectControl control = act.getControl();
+        makeMove(control);
         mMovesText.setText(act.getString(R.string.mo_placeholder,mCurrMove,mNumMoves));
         }
       });
@@ -187,7 +187,7 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void makeMove(ObjectPreRender pre)
+  private void makeMove(ObjectControl control)
     {
     if( mCanRotate )
       {
@@ -196,21 +196,21 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
       if( mCurrMove>mNumMoves )
         {
         mCurrMove= 0;
-        pre.initializeObject(null);
+        control.initializeObject(null);
         }
       else
         {
         int axis      = mMoves[mCurrMove-1][0];
 		    int rowBitmap = mMoves[mCurrMove-1][1];
 		    int bareAngle = mMoves[mCurrMove-1][2];
-		    int basicAngle= pre.getObject().getBasicAngle()[axis];
+		    int basicAngle= control.getObject().getBasicAngle()[axis];
         int angle     = bareAngle*(360/basicAngle);
         int duration  = Math.abs(angle)*MILLIS_PER_DEGREE;
 
         if( angle!=0 )
           {
           mCanRotate = false;
-          pre.addRotation(this, axis, rowBitmap, angle, duration);
+          control.addRotation(this, axis, rowBitmap, angle, duration);
           }
         else
           {
@@ -226,7 +226,7 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void backMove(ObjectPreRender pre)
+  private void backMove(ObjectControl control)
     {
     if( mCanRotate )
       {
@@ -235,21 +235,21 @@ public class RubikScreenSolution extends RubikScreenAbstract implements MovesFin
       if( mCurrMove<0 )
         {
         mCurrMove=mNumMoves;
-        pre.initializeObject(mMoves);
+        control.initializeObject(mMoves);
         }
       else
         {
         int axis      = mMoves[mCurrMove][0];
 		    int rowBitmap = mMoves[mCurrMove][1];
 		    int bareAngle = mMoves[mCurrMove][2];
-        int basicAngle= pre.getObject().getBasicAngle()[axis];
+        int basicAngle= control.getObject().getBasicAngle()[axis];
         int angle     = bareAngle*(360/basicAngle);
         int duration  = Math.abs(angle)*MILLIS_PER_DEGREE;
 
         if( angle!=0 )
           {
           mCanRotate = false;
-          pre.addRotation(this, axis, rowBitmap, -angle, duration);
+          control.addRotation(this, axis, rowBitmap, -angle, duration);
           }
         else
           {
diff --git a/src/main/java/org/distorted/screens/RubikScreenSolver.java b/src/main/java/org/distorted/screens/RubikScreenSolver.java
index 37afdbdb..0fd2808b 100644
--- a/src/main/java/org/distorted/screens/RubikScreenSolver.java
+++ b/src/main/java/org/distorted/screens/RubikScreenSolver.java
@@ -33,6 +33,7 @@ import android.view.View;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.main.ObjectType;
 
@@ -40,7 +41,6 @@ import org.distorted.dialogs.RubikDialogSolverError;
 import org.distorted.helpers.TransparentImageButton;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
-import org.distorted.objectlib.main.ObjectPreRender;
 import org.distorted.solvers.ImplementedSolversList;
 import org.distorted.solvers.SolverMain;
 
@@ -237,8 +237,8 @@ public class RubikScreenSolver extends RubikScreenAbstract
       @Override
       public void onClick(View v)
         {
-        ObjectPreRender pre = act.getPreRender();
-        pre.resetAllTextureMaps();
+        ObjectControl control = act.getControl();
+        control.resetAllTextureMaps();
         ScreenList.goBack(act);
         }
       });
diff --git a/src/main/java/org/distorted/tutorials/TutorialActivity.java b/src/main/java/org/distorted/tutorials/TutorialActivity.java
index 67c08ccd..7d94df8a 100644
--- a/src/main/java/org/distorted/tutorials/TutorialActivity.java
+++ b/src/main/java/org/distorted/tutorials/TutorialActivity.java
@@ -31,11 +31,10 @@ import android.widget.LinearLayout;
 import com.google.firebase.analytics.FirebaseAnalytics;
 
 import org.distorted.library.main.DistortedLibrary;
-
 import org.distorted.library.main.DistortedScreen;
-import org.distorted.objectlib.main.ObjectPreRender;
+
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.ObjectType;
-import org.distorted.objectlib.main.TwistyObject;
 import org.distorted.objectlib.helpers.BlockController;
 import org.distorted.objectlib.helpers.TwistyActivity;
 
@@ -210,7 +209,7 @@ public class TutorialActivity extends TwistyActivity
       if( mObjectOrdinal>=0 && mObjectOrdinal< ObjectType.NUM_OBJECTS )
         {
         ObjectType obj = ObjectType.getObject(mObjectOrdinal);
-        view.getPreRender().changeObject(obj);
+        view.getObjectControl().changeObject(obj);
         }
       }
     
@@ -247,15 +246,6 @@ public class TutorialActivity extends TwistyActivity
       return mFirebaseAnalytics;
       }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public TwistyObject getObject()
-      {
-      TutorialSurfaceView view = findViewById(R.id.rubikSurfaceView);
-      ObjectPreRender pre = view.getPreRender();
-      return pre.getObject();
-      }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     public int getScreenWidthInPixels()
@@ -272,10 +262,10 @@ public class TutorialActivity extends TwistyActivity
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public ObjectPreRender getPreRender()
+    public ObjectControl getControl()
       {
       TutorialSurfaceView view = findViewById(R.id.tutorialSurfaceView);
-      return view.getPreRender();
+      return view.getObjectControl();
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/tutorials/TutorialRenderer.java b/src/main/java/org/distorted/tutorials/TutorialRenderer.java
index b1a5c4be..063dc5f9 100644
--- a/src/main/java/org/distorted/tutorials/TutorialRenderer.java
+++ b/src/main/java/org/distorted/tutorials/TutorialRenderer.java
@@ -56,7 +56,7 @@ public class TutorialRenderer implements GLSurfaceView.Renderer, DistortedLibrar
    public void onDrawFrame(GL10 glUnused)
      {
      long time = System.currentTimeMillis();
-     mView.getPreRender().preRender();
+     mView.getObjectControl().preRender();
      mScreen.render(time);
      }
 
@@ -67,7 +67,6 @@ public class TutorialRenderer implements GLSurfaceView.Renderer, DistortedLibrar
       {
       mScreen.resize(width,height);
       mView.setScreenSize(width,height);
-      mView.getPreRender().setScreenSize(width);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/tutorials/TutorialScreen.java b/src/main/java/org/distorted/tutorials/TutorialScreen.java
index 815c6d70..ce250361 100644
--- a/src/main/java/org/distorted/tutorials/TutorialScreen.java
+++ b/src/main/java/org/distorted/tutorials/TutorialScreen.java
@@ -24,11 +24,11 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
 import org.distorted.helpers.MovesController;
-import org.distorted.objectlib.main.ObjectPreRender;
+import org.distorted.objectlib.main.ObjectControl;
 import org.distorted.objectlib.main.ObjectType;
+import org.distorted.objectlib.helpers.TwistyActivity;
 
 import org.distorted.helpers.LockController;
-import org.distorted.objectlib.helpers.TwistyActivity;
 import org.distorted.main.R;
 import org.distorted.main.RubikActivity;
 import org.distorted.screens.RubikScreenPlay;
@@ -55,7 +55,7 @@ public class TutorialScreen
       @Override
       public void onClick(View v)
         {
-        act.getPreRender().solveObject();
+        act.getControl().solveObject();
         mMovesController.clearMoves(act);
         }
       });
@@ -76,7 +76,7 @@ public class TutorialScreen
         RubikScreenPlay play = (RubikScreenPlay) ScreenList.PLAY.getScreenClass();
         int ordinal = play.getObject().ordinal();
         int numScrambles = ObjectType.getNumScramble(ordinal);
-        act.getPreRender().scrambleObject(numScrambles);
+        act.getControl().scrambleObject(numScrambles);
         }
       });
     }
@@ -93,8 +93,8 @@ public class TutorialScreen
       @Override
       public void onClick(View v)
         {
-        ObjectPreRender pre = act.getPreRender();
-        if( pre!=null ) pre.unblockEverything();
+        ObjectControl control = act.getControl();
+        if( control!=null ) control.unblockEverything();
         act.finish();
         }
       });
diff --git a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
index d0b4d568..f0bd49e6 100644
--- a/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
+++ b/src/main/java/org/distorted/tutorials/TutorialSurfaceView.java
@@ -30,7 +30,6 @@ import android.view.MotionEvent;
 import com.google.firebase.crashlytics.FirebaseCrashlytics;
 
 import org.distorted.objectlib.main.ObjectControl;
-import org.distorted.objectlib.main.ObjectPreRender;
 
 import static org.distorted.objectlib.main.ObjectControl.MODE_ROTATE;
 
@@ -67,9 +66,9 @@ public class TutorialSurfaceView extends GLSurfaceView
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    ObjectPreRender getPreRender()
+    ObjectControl getObjectControl()
       {
-      return mObjectController.getPreRender();
+      return mObjectController;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -83,8 +82,8 @@ public class TutorialSurfaceView extends GLSurfaceView
       if(!isInEditMode())
         {
         TutorialActivity act = (TutorialActivity)context;
-        mRenderer = new TutorialRenderer(this);
         mObjectController = new ObjectControl(act,new TutorialObjectStateActioner());
+        mRenderer = new TutorialRenderer(this);
 
         final ActivityManager activityManager= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
diff --git a/src/main/res/drawable-nodpi/ui_axis_arrow.png b/src/main/res/drawable-nodpi/ui_axis_arrow.png
deleted file mode 100644
index 3ba666ba..00000000
Binary files a/src/main/res/drawable-nodpi/ui_axis_arrow.png and /dev/null differ
diff --git a/src/main/res/drawable-nodpi/ui_fading_circle.png b/src/main/res/drawable-nodpi/ui_fading_circle.png
deleted file mode 100644
index a1c9ae21..00000000
Binary files a/src/main/res/drawable-nodpi/ui_fading_circle.png and /dev/null differ
diff --git a/src/main/res/drawable-nodpi/ui_hand_pointer.png b/src/main/res/drawable-nodpi/ui_hand_pointer.png
deleted file mode 100644
index 78cc84e6..00000000
Binary files a/src/main/res/drawable-nodpi/ui_hand_pointer.png and /dev/null differ
diff --git a/src/main/res/drawable-nodpi/ui_hand_shadow.png b/src/main/res/drawable-nodpi/ui_hand_shadow.png
deleted file mode 100644
index 58e737b8..00000000
Binary files a/src/main/res/drawable-nodpi/ui_hand_shadow.png and /dev/null differ
