commit 4888e97caade9173a95e6d4a2edddc5ea684cd49
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Mar 13 14:36:21 2020 +0000

    Major restructuring around making it possible to include different kinds of RubikObjects in the UI.

diff --git a/src/main/java/org/distorted/dialog/RubikDialogScores.java b/src/main/java/org/distorted/dialog/RubikDialogScores.java
index f0312f44..e3416c27 100644
--- a/src/main/java/org/distorted/dialog/RubikDialogScores.java
+++ b/src/main/java/org/distorted/dialog/RubikDialogScores.java
@@ -103,13 +103,22 @@ public class RubikDialogScores extends AppCompatDialogFragment
     tabLayout.setupWithViewPager(viewPager);
 
     viewPager.setCurrentItem(curTab);
+    RubikObjectList list;
 
-    for (int i = 0; i< RubikObjectList.LENGTH; i++)
+    for (int object=0; object<RubikObjectList.NUM_OBJECTS; object++)
       {
-      ImageView imageView = new ImageView(act);
-      imageView.setImageResource(RubikObjectList.getObject(i).getIconID());
-      TabLayout.Tab tab = tabLayout.getTabAt(i);
-      if(tab!=null) tab.setCustomView(imageView);
+      list = RubikObjectList.getObject(object);
+      int[] iconID = list.getIconIDs();
+      int len = list.getSizes().length;
+
+      for(int size=0; size<len; size++)
+        {
+        int t = RubikObjectList.pack(object,size);
+        ImageView imageView = new ImageView(act);
+        imageView.setImageResource(iconID[size]);
+        TabLayout.Tab tab = tabLayout.getTabAt(t);
+        if(tab!=null) tab.setCustomView(imageView);
+        }
       }
 
     return builder.create();
diff --git a/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
index 790eab4f..3fd1b43b 100644
--- a/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
+++ b/src/main/java/org/distorted/dialog/RubikDialogScoresPagerAdapter.java
@@ -28,7 +28,8 @@ import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import org.distorted.network.RubikScoresDownloader;
-import static org.distorted.object.RubikObjectList.LENGTH;
+import org.distorted.object.RubikObjectList;
+
 import static org.distorted.uistate.RubikStatePlay.MAX_SCRAMBLE;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -38,6 +39,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
   private FragmentActivity mAct;
   private RubikDialogScoresView[] mViews;
   private ViewPager mViewPager;
+  private int mNumTabs;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -49,7 +51,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
 
     addPage(mViews[c],country[c],name[c],time[c]);
 
-    for(int i=0; i<LENGTH; i++)
+    for(int i=0; i<mNumTabs; i++)
       {
       if( i==c ) continue;
 
@@ -66,7 +68,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
       @Override
       public void run()
         {
-        for(int i=0; i<LENGTH; i++)
+        for(int i=0; i<mNumTabs; i++)
           {
           mViews[i].prepareView(mAct);
           }
@@ -119,7 +121,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
       @Override
       public void run()
         {
-        for(int i=0; i<LENGTH; i++)
+        for(int i=0; i<mNumTabs; i++)
           {
           mViews[i].exception(exce);
           }
@@ -132,11 +134,12 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
   RubikDialogScoresPagerAdapter(FragmentActivity act, ViewPager viewPager)
     {
     mAct = act;
-    mViews = new RubikDialogScoresView[LENGTH];
+    mNumTabs = RubikObjectList.getTotal();
+    mViews = new RubikDialogScoresView[mNumTabs];
     mViewPager = viewPager;
 
     viewPager.setAdapter(this);
-    viewPager.setOffscreenPageLimit(LENGTH-1);
+    viewPager.setOffscreenPageLimit(mNumTabs-1);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -150,7 +153,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
 
     boolean allCreated = true;
 
-    for(int i=0; i<LENGTH; i++)
+    for(int i=0; i<mNumTabs; i++)
       {
       if( mViews[i]==null )
         {
@@ -181,7 +184,7 @@ class RubikDialogScoresPagerAdapter extends PagerAdapter implements RubikScoresD
   @Override
   public int getCount()
     {
-    return LENGTH;
+    return mNumTabs;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index 6885b84b..21a36b9c 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -73,8 +73,13 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       restorePreferences();
       RubikState.setState(this);
       RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
-      int ordinal = play.getButton();
-      view.getRenderer().createObject(RubikObjectList.getObject(ordinal));
+
+      int object = play.getObject();
+      int size   = play.getSize();
+      RubikObjectList obj = RubikObjectList.getObject(object);
+      int objectSize = obj.getSizes()[size];
+
+      view.getRenderer().createObject( obj, objectSize );
       }
     
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -93,17 +98,21 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
       {
       int id = v.getId();
 
-      if( id>=0 && id< RubikObjectList.LENGTH )
+      if( id>=0 && id< RubikObjectList.getTotal() )
         {
-        RubikObjectList object = RubikObjectList.getObject(id);
+        int object = RubikObjectList.unpackObject(id);
+        int size= RubikObjectList.unpackSize(id);
+
+        RubikObjectList obj = RubikObjectList.getObject(object);
+        int objectSize = obj.getSizes()[size];
 
         RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
-        boolean success = view.getRenderer().createObject(object);
+        boolean success = view.getRenderer().createObject(obj,objectSize);
 
         if( success )
           {
           RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
-          play.markButton(this,id);
+          play.markButton(this,object,size);
           }
         }
 
@@ -181,10 +190,11 @@ public class RubikActivity extends AppCompatActivity implements View.OnClickList
     public void Scores(View v)
       {
       RubikStatePlay play = (RubikStatePlay) RubikState.PLAY.getStateClass();
-      int tab = play.getButton();
+      int object = play.getObject();
+      int size   = play.getSize();
 
       Bundle bundle = new Bundle();
-      bundle.putInt("tab", tab);
+      bundle.putInt("tab", RubikObjectList.pack(object,size) );
 
       RubikDialogScores scores = new RubikDialogScores();
       scores.setArguments(bundle);
diff --git a/src/main/java/org/distorted/magic/RubikRenderer.java b/src/main/java/org/distorted/magic/RubikRenderer.java
index 648b0fe0..0bf6df09 100644
--- a/src/main/java/org/distorted/magic/RubikRenderer.java
+++ b/src/main/java/org/distorted/magic/RubikRenderer.java
@@ -44,6 +44,7 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
     private RubikSurfaceView mView;
     private DistortedScreen mScreen;
     private RubikObjectList mNextObject;
+    private int mNextSize;
     private int mScrambleObjectNum;
     private long mRotationFinishedID;
     private long[] mEffectID;
@@ -85,14 +86,14 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   private void createObjectNow(RubikObjectList object)
+   private void createObjectNow(RubikObjectList object, int size)
      {
      boolean firstTime = (mNewObject==null);
 
      if( mOldObject!=null ) mOldObject.releaseResources();
      mOldObject = mNewObject;
 
-     mNewObject = object.create(mView.getQuatCurrent(), mView.getQuatAccumulated());
+     mNewObject = object.create(size, mView.getQuatCurrent(), mView.getQuatAccumulated());
      mNewObject.createTexture();
      mView.setMovement(object.getObjectMovementClass());
 
@@ -154,12 +155,13 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-   boolean createObject(RubikObjectList object)
+   boolean createObject(RubikObjectList object, int size)
      {
-     if( mCanDrag && mCanRotate && object!=mNextObject )
+     if( mCanDrag && mCanRotate && (object!=mNextObject || mNextSize!=size) && size>0 )
        {
        mChangeObject = true;
        mNextObject = object;
+       mNextSize   = size;
        return true;
        }
 
@@ -286,7 +288,7 @@ public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
        mCanDrag      = false;
        mCanRotate    = false;
        mCanUI        = false;
-       createObjectNow(mNextObject);
+       createObjectNow(mNextObject, mNextSize);
        doEffectNow( BaseEffect.Type.SIZECHANGE );
        }
 
diff --git a/src/main/java/org/distorted/network/RubikScoresDownloader.java b/src/main/java/org/distorted/network/RubikScoresDownloader.java
index c2111f65..8226528c 100644
--- a/src/main/java/org/distorted/network/RubikScoresDownloader.java
+++ b/src/main/java/org/distorted/network/RubikScoresDownloader.java
@@ -19,12 +19,13 @@
 
 package org.distorted.network;
 
+import org.distorted.object.RubikObjectList;
+
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.UnknownHostException;
 
-import static org.distorted.object.RubikObjectList.LENGTH;
 import static org.distorted.uistate.RubikStatePlay.MAX_SCRAMBLE;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -86,12 +87,13 @@ public class RubikScoresDownloader implements Runnable
   private static String mUserName, mVersion;
   private static int mNumRuns;
 
+  private static int mTotal = RubikObjectList.getTotal();
   private static String mScores = "";
-  private static String[][][] mCountry = new String[LENGTH][MAX_SCRAMBLE][MAX_PLACES];
-  private static String[][][] mName    = new String[LENGTH][MAX_SCRAMBLE][MAX_PLACES];
-  private static String[][][] mTime    = new String[LENGTH][MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mCountry = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mName    = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
+  private static String[][][] mTime    = new String[mTotal][MAX_SCRAMBLE][MAX_PLACES];
 
-  private static int[][] mPlaces = new int[LENGTH][MAX_SCRAMBLE];
+  private static int[][] mPlaces = new int[mTotal][MAX_SCRAMBLE];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -99,7 +101,7 @@ public class RubikScoresDownloader implements Runnable
     {
     int begin=-1 ,end, len = mScores.length();
 
-    for(int i=0; i<LENGTH; i++)
+    for(int i=0; i<mTotal; i++)
       for(int j=0; j<MAX_SCRAMBLE; j++)
         {
         mPlaces[i][j] = 0;
@@ -127,14 +129,14 @@ public class RubikScoresDownloader implements Runnable
 
     if( s5>s4 && s4>s3 && s3>s2 && s2>s1 && s1>0 )
       {
-      int size = Integer.valueOf( row.substring(0,s1) );
+      int size = Integer.parseInt( row.substring(0,s1) );
 
-      if( size>=0 && size<LENGTH )
+      if( size>=0 && size<mTotal )
         {
-        int level      = Integer.valueOf( row.substring(s1+1,s2) );
-        int place      = Integer.valueOf( row.substring(s2+1,s3) );
+        int level      = Integer.parseInt( row.substring(s1+1,s2) );
+        int place      = Integer.parseInt( row.substring(s2+1,s3) );
         String name    = row.substring(s3+1, s4);
-        int time       = Integer.valueOf( row.substring(s4+1,s5) );
+        int time       = Integer.parseInt( row.substring(s4+1,s5) );
         String country = row.substring(s5+1, s6);
         String realTime= String.valueOf(time/10.0f);
 
diff --git a/src/main/java/org/distorted/object/RubikCube.java b/src/main/java/org/distorted/object/RubikCube.java
index ca2ee74e..f1481922 100644
--- a/src/main/java/org/distorted/object/RubikCube.java
+++ b/src/main/java/org/distorted/object/RubikCube.java
@@ -80,7 +80,7 @@ class RubikCube extends RubikObject
 
   RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
     {
-    super(size,quatCur,quatAcc,texture,mesh,effects);
+    super(size, 60, quatCur,quatAcc,texture,mesh,effects);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index 4f764dd6..ebf28a54 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -73,7 +73,7 @@ public abstract class RubikObject extends DistortedNode
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikObject(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture, MeshRectangles nodeMesh, DistortedEffects nodeEffects)
+  RubikObject(int size, int fov, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture, MeshRectangles nodeMesh, DistortedEffects nodeEffects)
     {
     super(nodeTexture,nodeEffects,nodeMesh);
 
@@ -122,7 +122,7 @@ public abstract class RubikObject extends DistortedNode
       attach(mCubits[i].mNode);
       }
 
-    setProjection(30, 0.1f);
+    setProjection(fov, 0.1f);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikObjectList.java b/src/main/java/org/distorted/object/RubikObjectList.java
index 2256968f..e1a40c9e 100644
--- a/src/main/java/org/distorted/object/RubikObjectList.java
+++ b/src/main/java/org/distorted/object/RubikObjectList.java
@@ -29,28 +29,39 @@ import org.distorted.magic.R;
 
 public enum RubikObjectList
   {
-  CUBE2 ( 2, R.drawable.cube2, RubikCube.class, RubikCubeMovement.class),
-  CUBE3 ( 3, R.drawable.cube3, RubikCube.class, RubikCubeMovement.class),
-  CUBE4 ( 4, R.drawable.cube4, RubikCube.class, RubikCubeMovement.class),
-  CUBE5 ( 5, R.drawable.cube5, RubikCube.class, RubikCubeMovement.class),
+  CUBE     ( new int[] {2,3,4},
+             new int[] {R.drawable.cube2,R.drawable.cube3,R.drawable.cube4},
+             new RubikCubeMovement() ),
+  PYRAMINX ( new int[] {3,4},
+             new int[] {R.drawable.pyra3,R.drawable.pyra4},
+             new RubikPyraminxMovement() ),
   ;
 
-  public static final int LENGTH = values().length;
-  private final int mObjectSize, mIconID;
-  final Class<? extends RubikObject> mObjectClass;
-  final Class<? extends RubikObjectMovement> mObjectMovementClass;
+  public static final int NUM_OBJECTS = values().length;
+  public static final int MAX_SIZE;
+
+  private final int[] mObjectSizes, mIconIDs;
+  final RubikObjectMovement mObjectMovementClass;
   private static final RubikObjectList[] objects;
+  private static int mNumAll;
 
   static
     {
-    int i = 0;
-    objects = new RubikObjectList[LENGTH];
+    mNumAll = 0;
+    int size, i = 0;
+    objects = new RubikObjectList[NUM_OBJECTS];
+    int maxsize = Integer.MIN_VALUE;
 
-    for(RubikObjectList size: RubikObjectList.values())
+    for(RubikObjectList object: RubikObjectList.values())
       {
-      objects[i] = size;
+      objects[i] = object;
       i++;
+      size = object.mObjectSizes.length;
+      mNumAll += size;
+      if( size> maxsize ) maxsize = size;
       }
+
+    MAX_SIZE = maxsize;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -62,38 +73,97 @@ public enum RubikObjectList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  RubikObjectList(int size, int iconID, Class<? extends RubikObject > object, Class<? extends RubikObjectMovement> movement)
+  public static int pack(int object, int size)
+    {
+    int ret = 0;
+    for(int i=0; i<object; i++) ret += objects[i].mObjectSizes.length;
+
+    return ret+size;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static int unpackSize(int number)
+    {
+    int num;
+
+    for(int i=0; i<NUM_OBJECTS; i++)
+      {
+      num = objects[i].mObjectSizes.length;
+      if( number<num ) return number;
+      number -= num;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static int unpackObject(int number)
     {
-    mObjectSize = size;
-    mIconID     = iconID;
-    mObjectClass= object;
+    int num;
+
+    for(int i=0; i<NUM_OBJECTS; i++)
+      {
+      num = objects[i].mObjectSizes.length;
+      if( number<num ) return i;
+      number -= num;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static int getTotal()
+    {
+    return mNumAll;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikObjectList(int[] sizes, int[] iconIDs, RubikObjectMovement movement)
+    {
+    mObjectSizes= sizes;
+    mIconIDs    = iconIDs;
     mObjectMovementClass = movement;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int getIconID()
+  public int[] getIconIDs()
     {
-    return mIconID;
+    return mIconIDs;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO - currently all objects in the list are RubikCubes of various sizes
 
-  public RubikObject create(Static4D quatCur, Static4D quatAcc)
+  public int[] getSizes()
+    {
+    return mObjectSizes;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RubikObject create(int size, Static4D quatCur, Static4D quatAcc)
     {
     DistortedTexture texture = new DistortedTexture();
     DistortedEffects effects = new DistortedEffects();
     MeshRectangles mesh      = new MeshRectangles(20,20);   // mesh of the node, not of the cubits
 
-    return new RubikPyraminx(mObjectSize, quatCur, quatAcc, texture, mesh, effects);
+    switch(ordinal())
+      {
+      case 0: return new RubikCube    (size, quatCur, quatAcc, texture, mesh, effects);
+      case 1: return new RubikPyraminx(size, quatCur, quatAcc, texture, mesh, effects);
+      }
+
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO - currently all objects in the list are RubikCubes of various sizes
 
   public RubikObjectMovement getObjectMovementClass()
     {
-    return new RubikPyraminxMovement();
+    return mObjectMovementClass;
     }
   }
diff --git a/src/main/java/org/distorted/object/RubikPyraminx.java b/src/main/java/org/distorted/object/RubikPyraminx.java
index 2116ca80..ac27c237 100644
--- a/src/main/java/org/distorted/object/RubikPyraminx.java
+++ b/src/main/java/org/distorted/object/RubikPyraminx.java
@@ -85,7 +85,7 @@ public class RubikPyraminx extends RubikObject
 
   RubikPyraminx(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
     {
-    super(size,quatCur,quatAcc,texture,mesh,effects);
+    super(size, 30, quatCur,quatAcc,texture,mesh,effects);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/uistate/RubikStatePlay.java b/src/main/java/org/distorted/uistate/RubikStatePlay.java
index 1e089151..6694bd2a 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePlay.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePlay.java
@@ -43,11 +43,13 @@ public class RubikStatePlay extends RubikStateAbstract
   private static final int MIN_SCRAMBLE =  1;
   private static final int DEF_SCRAMBLE =  1;
   public  static final int MAX_SCRAMBLE = 18;
-  private static final int DEF_BUTTON   = RubikObjectList.CUBE3.ordinal();
+  private static final int DEF_OBJECT   = RubikObjectList.CUBE.ordinal();
+  private static final int DEF_SIZE     =  1;  // i.e. the second from the list of CUBE's sizes
 
   private HorizontalNumberPicker mPicker;
   private int mPickerValue;
-  private int mButton = DEF_BUTTON;
+  private int mObject = DEF_OBJECT;
+  private int mSize   = DEF_SIZE;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -79,16 +81,21 @@ public class RubikStatePlay extends RubikStateAbstract
     int padding = (int)(3*scale + 0.5f);
     ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
 
-    for(int i = 0; i< RubikObjectList.LENGTH; i++)
+    for(int i=0; i< RubikObjectList.NUM_OBJECTS; i++)
       {
-      ImageButton button = new ImageButton(act);
-      button.setLayoutParams(params);
-      button.setId(i);
-      button.setPadding(padding,0,padding,0);
-      int iconID = RubikObjectList.getObject(i).getIconID();
-      button.setImageResource(iconID);
-      button.setOnClickListener(act);
-      layoutBot.addView(button);
+      int[] iconIDs = RubikObjectList.getObject(i).getIconIDs();
+      int len = iconIDs.length;
+
+      for(int s=0; s<len; s++)
+        {
+        ImageButton button = new ImageButton(act);
+        button.setLayoutParams(params);
+        button.setId(RubikObjectList.pack(i,s));
+        button.setPadding(padding,0,padding,0);
+        button.setImageResource(iconIDs[s]);
+        button.setOnClickListener(act);
+        layoutBot.addView(button);
+        }
       }
 
     ViewGroup.LayoutParams params2 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,size);
@@ -101,7 +108,7 @@ public class RubikStatePlay extends RubikStateAbstract
     button.setOnClickListener(act);
     layoutBot.addView(button);
 
-    markButton(act,mButton);
+    markButton(act,mObject,mSize);
 
     mPicker = act.findViewById(R.id.rubikNumberPicker);
 
@@ -119,18 +126,20 @@ public class RubikStatePlay extends RubikStateAbstract
     {
     if( mPicker!=null )
       {
-      editor.putInt("scramble", mPicker.getValue() );
+      editor.putInt("statePlay_scramble", mPicker.getValue() );
       }
 
-    editor.putInt("button", mButton);
+    editor.putInt("statePlay_object", mObject);
+    editor.putInt("statePlay_size"  , mSize);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void restorePreferences(SharedPreferences preferences)
     {
-    mPickerValue= preferences.getInt("scramble", DEF_SCRAMBLE);
-    mButton     = preferences.getInt("button"  , DEF_BUTTON  );
+    mPickerValue= preferences.getInt("statePlay_scramble", DEF_SCRAMBLE);
+    mObject     = preferences.getInt("statePlay_object"  , DEF_OBJECT  );
+    mSize       = preferences.getInt("statePlay_size"    , DEF_SIZE    );
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -142,22 +151,33 @@ public class RubikStatePlay extends RubikStateAbstract
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public int getButton()
+  public int getObject()
     {
-    return mButton;
+    return mObject;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void markButton(RubikActivity act, int button)
+  public int getSize()
     {
-    mButton = button;
+    return mSize;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markButton(RubikActivity act, int object, int size)
+    {
+    mObject = object;
+    mSize   = size;
+
+    int lookingFor = RubikObjectList.pack(object,size);
+    int len = RubikObjectList.getTotal();
 
-    for(int b = 0; b< RubikObjectList.LENGTH; b++)
+    for(int button=0; button<len; button++)
       {
-      Drawable d = act.findViewById(b).getBackground();
+      Drawable d = act.findViewById(button).getBackground();
 
-      if( b==button )
+      if( button==lookingFor )
         {
         d.setColorFilter(ContextCompat.getColor(act,R.color.red), PorterDuff.Mode.MULTIPLY);
         }
diff --git a/src/main/java/org/distorted/uistate/RubikStateSolving.java b/src/main/java/org/distorted/uistate/RubikStateSolving.java
index f64f9d2a..ca3a560c 100644
--- a/src/main/java/org/distorted/uistate/RubikStateSolving.java
+++ b/src/main/java/org/distorted/uistate/RubikStateSolving.java
@@ -28,12 +28,14 @@ import android.widget.TextView;
 
 import org.distorted.magic.R;
 import org.distorted.magic.RubikActivity;
+import org.distorted.object.RubikObjectList;
 
 import java.util.Timer;
 import java.util.TimerTask;
 
 import static android.view.View.INVISIBLE;
-import static org.distorted.object.RubikObjectList.LENGTH;
+import static org.distorted.object.RubikObjectList.NUM_OBJECTS;
+import static org.distorted.object.RubikObjectList.MAX_SIZE;
 import static org.distorted.uistate.RubikStatePlay.MAX_SCRAMBLE;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -47,19 +49,20 @@ public class RubikStateSolving extends RubikStateAbstract
   private long mStartTime;
   private boolean mRunning;
 
-  private long[][] mRecords;
+  private long[][][] mRecords;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   RubikStateSolving()
     {
-    mRecords = new long[LENGTH][MAX_SCRAMBLE];
+    mRecords = new long[NUM_OBJECTS][MAX_SIZE][MAX_SCRAMBLE];
 
-    for(int i=0; i<LENGTH; i++)
-      for(int j=0; j<MAX_SCRAMBLE; j++)
-        {
-        mRecords[i][j] = NO_RECORD;
-        }
+    for(int i=0; i<NUM_OBJECTS; i++)
+      for(int j=0; j<MAX_SIZE; j++)
+        for(int k=0; k<MAX_SCRAMBLE; k++)
+          {
+          mRecords[i][j][k] = NO_RECORD;
+          }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -115,25 +118,27 @@ public class RubikStateSolving extends RubikStateAbstract
 
   public void savePreferences(SharedPreferences.Editor editor)
     {
-    for(int i=0; i<LENGTH; i++)
-      for(int j=0; j<MAX_SCRAMBLE; j++)
-        {
-        if( mRecords[i][j]!=NO_RECORD)
+    for(int i=0; i<NUM_OBJECTS; i++)
+      for(int j=0; j<MAX_SIZE; j++)
+        for(int k=0; k<MAX_SCRAMBLE; k++)
           {
-          editor.putLong("record_"+i+"_"+j, mRecords[i][j]);
+          if( mRecords[i][j][k]!=NO_RECORD)
+            {
+            editor.putLong("record_"+i+"_"+j+"_"+k, mRecords[i][j][k]);
+            }
           }
-        }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public void restorePreferences(SharedPreferences preferences)
     {
-    for(int i=0; i<LENGTH; i++)
-      for(int j=0; j<MAX_SCRAMBLE; j++)
-        {
-        mRecords[i][j] = preferences.getLong("record_"+i+"_"+j, NO_RECORD );
-        }
+    for(int i=0; i<NUM_OBJECTS; i++)
+      for(int j=0; j<MAX_SIZE; j++)
+        for(int k=0; k<MAX_SCRAMBLE; k++)
+          {
+          mRecords[i][j][k] = preferences.getLong("record_"+i+"_"+j+"_"+k, NO_RECORD );
+          }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -181,14 +186,16 @@ public class RubikStateSolving extends RubikStateAbstract
       long timeTaken = System.currentTimeMillis()-mStartTime;
 
       RubikStatePlay play = (RubikStatePlay)RubikState.PLAY.getStateClass();
-      int object  = play.getButton();
+      int object  = play.getObject();
+      int size    = play.getSize();
       int scramble= play.getPicker();
+      int maxsize = RubikObjectList.getObject(object).getSizes().length;
 
-      if( object>=0 && object<LENGTH && scramble>=0 && scramble<MAX_SCRAMBLE )
+      if( object>=0 && object< NUM_OBJECTS && size>=0 && size<maxsize && scramble>=0 && scramble<MAX_SCRAMBLE )
         {
-        if( mRecords[object][scramble]> timeTaken )
+        if( mRecords[object][size][scramble]> timeTaken )
           {
-          mRecords[object][scramble] = timeTaken;
+          mRecords[object][size][scramble] = timeTaken;
           android.util.Log.e("solv","new record!");
           }
         }
diff --git a/src/main/res/drawable/pyra2.png b/src/main/res/drawable/pyra2.png
new file mode 100644
index 00000000..239d380a
Binary files /dev/null and b/src/main/res/drawable/pyra2.png differ
diff --git a/src/main/res/drawable/pyra3.png b/src/main/res/drawable/pyra3.png
new file mode 100644
index 00000000..e7fa6640
Binary files /dev/null and b/src/main/res/drawable/pyra3.png differ
diff --git a/src/main/res/drawable/pyra4.png b/src/main/res/drawable/pyra4.png
new file mode 100644
index 00000000..11b2e7fa
Binary files /dev/null and b/src/main/res/drawable/pyra4.png differ
diff --git a/src/main/res/drawable/pyra5.png b/src/main/res/drawable/pyra5.png
new file mode 100644
index 00000000..55aa5dba
Binary files /dev/null and b/src/main/res/drawable/pyra5.png differ
