commit 7ff3899705ab28a57539d1258a8edff05516e06e
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Sep 6 22:10:50 2021 +0200

    Remove statics from the Cube classes.

diff --git a/src/main/java/org/distorted/helpers/ScrambleStateSquare1.java b/src/main/java/org/distorted/helpers/ScrambleStateSquare1.java
new file mode 100644
index 00000000..3fb87092
--- /dev/null
+++ b/src/main/java/org/distorted/helpers/ScrambleStateSquare1.java
@@ -0,0 +1,594 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.helpers;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// producer of the ScrambleStateGraph - but only for a single Twisty Puzzle, the 'Square-1'.
+
+public class ScrambleStateSquare1
+{
+  private static final int INVALID_MOVE = -1;
+  private static final int NUM_MOVES = 23;
+
+  private int mDist;
+  private boolean mFresh;
+  private long mID;
+  private final long[] mMoves;
+
+  private final int[][] mPermittedAngles;
+  private final int[] mCornerQuat, mTmp;
+
+  // QUATS[i]*QUATS[j] = QUATS[QUAT_MULT[i][j]]
+  static final int[][] QUAT_MULT = new int[][]
+    {
+      {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,},
+      {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12,},
+      {  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13,},
+      {  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14,},
+      {  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3, 16, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15,},
+      {  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4, 17, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16,},
+      {  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5, 18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17,},
+      {  7,  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18,},
+      {  8,  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19,},
+      {  9, 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8, 21, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20,},
+      { 10, 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 22, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,},
+      { 11,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 23, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,},
+      { 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,},
+      { 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,},
+      { 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16, 15,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,  3,},
+      { 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17, 16,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,  4,},
+      { 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18, 17,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,  5,},
+      { 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19, 18,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,  6,},
+      { 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20, 19,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,  7,},
+      { 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21, 20,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,  8,},
+      { 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22, 21,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,  9,},
+      { 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11, 10,},
+      { 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 11,},
+      { 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,}
+    };
+
+  // quat indices that make corner cubits bandage the puzzle.
+  private static final int[][] BAD_CORNER_QUATS = new int[][]
+    {
+      { 2, 8,17,23},
+      { 5,11,14,20},
+    };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ScrambleStateSquare1(long id, int dist)
+    {
+    mCornerQuat = new int[8];
+    mTmp        = new int[8];
+    mPermittedAngles = new int[2][12];
+
+    mDist = dist;
+    mFresh = true;
+    mID = id;
+    mMoves = createMoves(mID);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setNotFresh()
+    {
+    mFresh = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean isFresh()
+    {
+    return mFresh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void computeGraph()
+    {
+    ArrayList<ScrambleStateSquare1> graph;
+
+    ScrambleStateSquare1 bsg = new ScrambleStateSquare1(0,0);
+    graph = new ArrayList<>();
+    graph.add(bsg);
+
+android.util.Log.e("D", "INSERTING");
+
+    insertChildren(graph,0);
+
+//android.util.Log.e("D", "PRUNING "+graph.size());
+
+//    pruneGraph(graph);
+
+android.util.Log.e("D", "REMAPPING "+graph.size());
+
+    remapGraph(graph);
+
+    int num = graph.size();
+    android.util.Log.e("D", "\n"+num+" states\n");
+
+    for(int i=0; i<num; i++)
+      {
+      bsg = graph.get(i);
+      android.util.Log.e("D", formatMoves(bsg));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void insertChildren(ArrayList<ScrambleStateSquare1> list, long id)
+    {
+    ScrambleStateSquare1 bsg = findState(list,id);
+
+    if( bsg==null )
+      {
+      android.util.Log.e("D", "error: "+id+" doesn't exist");
+      return;
+      }
+
+    int dist = bsg.mDist;
+    if( dist>6 ) return;
+
+    for(int i=0; i<NUM_MOVES; i++)
+      {
+      long moveID = bsg.getMoveID(i);
+
+      if( moveID!=INVALID_MOVE )
+        {
+        ScrambleStateSquare1 tmp = findState(list,moveID);
+
+        if( tmp==null )
+          {
+          tmp = new ScrambleStateSquare1(moveID,dist+1);
+          list.add(tmp);
+          }
+        }
+      }
+
+    for(int i=0; i<NUM_MOVES; i++)
+      {
+      long moveID = bsg.getMoveID(i);
+
+      if( moveID!=INVALID_MOVE )
+        {
+        ScrambleStateSquare1 tmp = findState(list,moveID);
+
+        if( tmp.isFresh() )
+          {
+          tmp.setNotFresh();
+          insertChildren(list,moveID);
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void pruneGraph(ArrayList<ScrambleStateSquare1> list)
+    {
+    int num = list.size(), numAxis;
+    boolean pruned = false;
+    ScrambleStateSquare1 bsg;
+
+    for(int i=0; i<num; i++)
+      {
+      bsg = list.get(i);
+      numAxis = bsg.numAxis();
+
+      if( numAxis<2 )
+        {
+        list.remove(i);
+        long id = bsg.getID();
+        pruned = true;
+        remapID(list,id,INVALID_MOVE);
+        break;
+        }
+      }
+
+    if( pruned ) pruneGraph(list);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void remapGraph(ArrayList<ScrambleStateSquare1> list)
+    {
+    long id;
+    int num = list.size();
+    ScrambleStateSquare1 bsg;
+
+    for(int i=0; i<num; i++ )
+      {
+      bsg = list.get(i);
+      id = bsg.getID();
+      bsg.setID(i);
+      remapID(list,id,i);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void remapID(ArrayList<ScrambleStateSquare1> list, long id, long newId)
+    {
+    ScrambleStateSquare1 bsg;
+    int size = list.size();
+
+    for(int i=0; i<size; i++)
+      {
+      bsg = list.get(i);
+
+      for(int j=0; j<NUM_MOVES; j++)
+        {
+        if( bsg.getMoveID(j)==id ) bsg.setMoveID(j,newId);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static ScrambleStateSquare1 findState(ArrayList<ScrambleStateSquare1> list, long id)
+    {
+    ScrambleStateSquare1 bsg;
+    int num = list.size();
+
+    for(int i=0; i<num; i++)
+      {
+      bsg= list.get(i);
+      if( bsg.getID() == id ) return bsg;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String formatMoves(ScrambleStateSquare1 bsg)
+    {
+    String x = getTable(bsg,0);
+    String y = getTable(bsg,11);
+    String z = getTable(bsg,12);
+
+    //return "    new ScrambleStateGraph( new int[][] { "+x+", "+y+", "+z+" } ),";
+
+    long id = bsg.getID();
+
+    return id+" "+x+" , "+y+" , "+z;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String getTable(ScrambleStateSquare1 sc, int index)
+    {
+    String ret = "";
+    long m;
+    int half = (NUM_MOVES)/2;
+
+    if( index==11 )
+      {
+      m = sc.getMoveID(index);
+      if( m==INVALID_MOVE ) return formatL("{}");
+      ret += formatRet(ret,0,1,m);
+      }
+    else
+      {
+      for(int i=0; i<half; i++)
+        {
+        m = sc.getMoveID(index+i);
+        int row = index==0 ? 0:2;
+        int angle = i+1;
+        if( m!=INVALID_MOVE ) ret += formatRet(ret,row,angle,m);
+        }
+      }
+
+    return formatL("{" + ret + "}");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String formatRet(String str, int row, int angle, long id)
+    {
+    String ret = str.length()!=0 ? "  ,":"  ";
+
+    ret += row;
+    ret += angle<0 ? "," : ",";
+    ret += angle;
+
+         if( id< 10 ) ret += (",  "+id);
+    else if( id<100 ) ret += (", " +id);
+    else              ret += (","  +id);
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static final int LENGTH = 28;
+
+  private static String formatL(String input)
+    {
+    int len = input.length();
+    String ret = input;
+    for(int i=0 ;i<LENGTH-len; i++) ret += " ";
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private long getID()
+    {
+    return mID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setID(long id)
+    {
+    mID = id;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private long getMoveID(int index)
+    {
+    return (index>=0 && index<NUM_MOVES) ? mMoves[index] : -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setMoveID(int index, long newID)
+    {
+    if( index>=0 && index<NUM_MOVES ) mMoves[index] = newID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numAxis()
+    {
+    int num = 0;
+    int half = (NUM_MOVES)/2;
+
+    for(int i=0; i<half; i++)
+      {
+      if( mMoves[i]!=INVALID_MOVE )
+        {
+        num++;
+        break;
+        }
+      }
+
+    for(int i=half; i<NUM_MOVES-1; i++)
+      {
+      if( mMoves[i]!=INVALID_MOVE )
+        {
+        num++;
+        break;
+        }
+      }
+
+    if( mMoves[NUM_MOVES-1]!=INVALID_MOVE ) num++;
+
+    return num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private long[] createMoves(long id)
+    {
+    long[] ret = new long[NUM_MOVES];
+    fillCornerQuat(id);
+    computePermittedAngles();
+
+    generateUMoves(ret, 0);
+    generateSMoves(ret,11);
+    generateLMoves(ret,12);
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void generateUMoves(long[] table, int index)
+    {
+    for(int angle=1; angle<12; angle++)
+      {
+      table[index+angle-1] = mPermittedAngles[1][angle]==1 ? updateCornerQuats(0,2,angle) : INVALID_MOVE;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void generateLMoves(long[] table, int index)
+    {
+    for(int angle=1; angle<12; angle++)
+      {
+      table[index+angle-1] = mPermittedAngles[0][angle]==1 ? updateCornerQuats(0,0,angle) : INVALID_MOVE;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void generateSMoves(long[] table, int index)
+    {
+    table[index] = updateCornerQuats(1,1,1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillCornerQuat(long id)
+    {
+    int NUM_QUATS = 24;
+
+    for(int i=0; i<8; i++)
+      {
+      mCornerQuat[i] = (int)(id%NUM_QUATS);
+      id/=NUM_QUATS;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static long returnID(int[] cornerQuat)
+    {
+    int NUM_QUATS = 24;
+    long mult=1,ret=0;
+
+    for(int i=0; i<8; i++)
+      {
+      ret += mult*cornerQuat[i];
+      mult*=NUM_QUATS;
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean cornerIsUp(int index)
+    {
+    return ((index<4) ^ (mCornerQuat[index]>=12));
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean cornerIsLeft(int index)
+    {
+    int q = mCornerQuat[index];
+
+    switch(index)
+      {
+      case 0:
+      case 4: return ((q>=3 && q<= 7) || (q>=18 && q<=22));
+      case 1:
+      case 5: return ((q>=6 && q<=10) || (q>=15 && q<=19));
+      case 2:
+      case 6: return ((q==0 || q==1 || (q>=9 && q<=11)) || (q>=12 && q<=16));
+      case 3:
+      case 7: return ((q>=0 && q<=4) || (q==12 || q==13 || (q>=21 && q<=23)));
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int makeQuat(int axis,int index)
+    {
+    if( axis==1 ) return 13;
+    if( index<0 ) index+=12;
+    return index;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean cornerBelongs(int index, int axis, int layer)
+    {
+    if( axis==0 )
+      {
+      boolean up = cornerIsUp(index);
+      return ((up && layer==2) || (!up && layer==0));
+      }
+    else
+      {
+      boolean le = cornerIsLeft(index);
+      return ((le && layer==0) || (!le && layer==1));
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private long updateCornerQuats(int axis, int layer, int index)
+    {
+    int quat = makeQuat(axis,index);
+
+    for(int corner=0; corner<8; corner++)
+      {
+      if( cornerBelongs(corner,axis,layer) )
+        {
+        int curr = mCornerQuat[corner];
+        mTmp[corner] = QUAT_MULT[quat][curr];
+        }
+      else
+        {
+        mTmp[corner] = mCornerQuat[corner];
+        }
+      }
+
+    return returnID(mTmp);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean quatIsBad(int quatIndex, int corner)
+    {
+    int index = (corner%2);
+
+    return ( quatIndex==BAD_CORNER_QUATS[index][0] ||
+             quatIndex==BAD_CORNER_QUATS[index][1] ||
+             quatIndex==BAD_CORNER_QUATS[index][2] ||
+             quatIndex==BAD_CORNER_QUATS[index][3]  );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int isPermittedDo(int angle)
+    {
+    for(int corner=0; corner<8; corner++)
+      {
+      if( !cornerIsUp(corner) )
+        {
+        int currQuat = mCornerQuat[corner];
+        int finalQuat= QUAT_MULT[angle][currQuat];
+        if( quatIsBad(finalQuat,corner) ) return 0;
+        }
+      }
+
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int isPermittedUp(int angle)
+    {
+    for(int corner=0; corner<8; corner++)
+      {
+      if( cornerIsUp(corner) )
+        {
+        int currQuat = mCornerQuat[corner];
+        int finalQuat= QUAT_MULT[angle][currQuat];
+        if( quatIsBad(finalQuat,corner) ) return 0;
+        }
+      }
+
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computePermittedAngles()
+    {
+    for(int angle=0; angle<12; angle++)
+      {
+      mPermittedAngles[0][angle] = isPermittedDo(angle);
+      mPermittedAngles[1][angle] = isPermittedUp(angle);
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objects/Cubit.java b/src/main/java/org/distorted/objects/Cubit.java
index ffcc1a14..90d44562 100644
--- a/src/main/java/org/distorted/objects/Cubit.java
+++ b/src/main/java/org/distorted/objects/Cubit.java
@@ -76,12 +76,12 @@ class Cubit
     float xd,yd,zd,wd;
     float diff, mindiff = Float.MAX_VALUE;
     int ret=0;
-    int num_quats = mParent.QUATS.length;
+    int num_quats = mParent.OBJECT_QUATS.length;
     Static4D qt;
 
     for(int q=0; q<num_quats; q++)
       {
-      qt = mParent.QUATS[q];
+      qt = mParent.OBJECT_QUATS[q];
 
       xd = x - qt.get0();
       yd = y - qt.get1();
@@ -189,7 +189,7 @@ class Cubit
 
   int removeRotationNow(Static4D quat)
     {
-    Static4D q = QuatHelper.quatMultiply(quat,mParent.QUATS[mQuatIndex]);
+    Static4D q = QuatHelper.quatMultiply(quat,mParent.OBJECT_QUATS[mQuatIndex]);
     mQuatIndex = normalizeScrambleQuat(q);
 
     modifyCurrentPosition(quat);
diff --git a/src/main/java/org/distorted/objects/TwistyCube.java b/src/main/java/org/distorted/objects/TwistyCube.java
index 9f6284aa..a743ce73 100644
--- a/src/main/java/org/distorted/objects/TwistyCube.java
+++ b/src/main/java/org/distorted/objects/TwistyCube.java
@@ -37,7 +37,6 @@ import java.util.Random;
 
 class TwistyCube extends TwistyObject
 {
-  // the three rotation axis of a RubikCube. Must be normalized.
   static final Static3D[] ROT_AXIS = new Static3D[]
          {
            new Static3D(1,0,0),
@@ -45,8 +44,6 @@ class TwistyCube extends TwistyObject
            new Static3D(0,0,1)
          };
 
-  private static final int[] BASIC_ANGLE = new int[] { 4,4,4 };
-
   private static final int[] FACE_COLORS = new int[]
          {
            COLOR_YELLOW, COLOR_WHITE,
@@ -54,86 +51,14 @@ class TwistyCube extends TwistyObject
            COLOR_RED   , COLOR_ORANGE
          };
 
-  // All legal rotation quats of a RubikCube of any size.
-  // Here's how to compute this:
-  // 1) compute how many rotations there are (RubikCube of any size = 24)
-  // 2) take the AXIS, angles of rotation (90 in RubikCube's case) compute the basic quaternions
-  // (i.e. rotations of 1 basic angle along each of the axis) and from there start semi-randomly
-  // multiplying them and eventually you'll find all (24) legal rotations.
-  // Example program in C, res/raw/compute_quats.c , is included.
-  private static final Static4D[] QUATS = new Static4D[]
-         {
-         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
-         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
-         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
-         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
-
-         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
-         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
-         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
-         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
-         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
-         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
-         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
-         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
-         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
-         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
-         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
-         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
-
-         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
-         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
-         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
-         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
-         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
-         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
-         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
-         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
-         };
-
-  private static final double[][] VERTICES = new double[][]
-          {
-              { 0.5, 0.5, 0.5 },
-              { 0.5, 0.5,-0.5 },
-              { 0.5,-0.5, 0.5 },
-              { 0.5,-0.5,-0.5 },
-              {-0.5, 0.5, 0.5 },
-              {-0.5, 0.5,-0.5 },
-              {-0.5,-0.5, 0.5 },
-              {-0.5,-0.5,-0.5 },
-          };
-
-  private static final int[][] VERT_INDEXES = new int[][]
-          {
-              {2,3,1,0},   // counterclockwise!
-              {7,6,4,5},
-              {4,0,1,5},
-              {7,3,2,6},
-              {6,2,0,4},
-              {3,7,5,1}
-          };
-
-  private static final float[][] STICKERS = new float[][]
-          {
-              { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f }
-          };
-
-  private static final ObjectSticker[] mStickers;
-
-  static
-    {
-    final float radius = 0.10f;
-    final float stroke = 0.08f;
-    final float[] radii = {radius,radius,radius,radius};
-    mStickers = new ObjectSticker[STICKERS.length];
-    mStickers[0] = new ObjectSticker(STICKERS[0],null,radii,stroke );
-    }
-
   private int mCurrState;
   private int mIndexExcluded;
   private final ScrambleState[] mStates;
   private int[][] mScrambleTable;
   private int[] mNumOccurences;
+  private Static4D[] mQuats;
+  private int[] mBasicAngle;
+  private ObjectSticker[] mStickers;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -188,12 +113,48 @@ class TwistyCube extends TwistyObject
     return ret;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void initializeQuats()
+    {
+    mQuats = new Static4D[]
+         {
+         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
+         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
+         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
+         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
+
+         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
+         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
+         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
+         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
+         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
+         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
+         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
+         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
+         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
+         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
+         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
+         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
+
+         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
+         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
+         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
+         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
+         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
+         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
+         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
+         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
+         };
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int[] getSolvedQuats(int cubit, int numLayers)
     {
+    if( mQuats ==null ) initializeQuats();
     int status = retCubitSolvedStatus(cubit,numLayers);
-    return status<0 ? null : buildSolvedQuats(MovementCube.FACE_AXIS[status],QUATS);
+    return status<0 ? null : buildSolvedQuats(MovementCube.FACE_AXIS[status], mQuats);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -211,6 +172,28 @@ class TwistyCube extends TwistyObject
       default: num = 5; extraI = 0; extraV = 0; height = 0.045f; break;
       }
 
+    double[][] vertices = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+    int[][] vert_indices = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
     float[][] bands     = new float[][] { {height,35,0.5f,0.7f,num,extraI,extraV} };
     int[] bandIndices   = new int[] { 0,0,0,0,0,0};
     float[][] corners   = new float[][] { {0.036f,0.12f} };
@@ -218,14 +201,15 @@ class TwistyCube extends TwistyObject
     float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
     int[] centerIndices = new int[] { 0,0,0,0,0,0,0,0 };
 
-    return new ObjectShape(VERTICES,VERT_INDEXES,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
+    return new ObjectShape(vertices,vert_indices,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   Static4D getQuat(int cubit, int numLayers)
     {
-    return QUATS[0];
+    if( mQuats ==null ) initializeQuats();
+    return mQuats[0];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -253,6 +237,16 @@ class TwistyCube extends TwistyObject
 
   ObjectSticker retSticker(int face)
     {
+    if( mStickers==null )
+      {
+      final float[][] STICKERS = new float[][]  { { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f } };
+      final float radius = 0.10f;
+      final float stroke = 0.08f;
+      final float[] radii = {radius,radius,radius,radius};
+      mStickers = new ObjectSticker[STICKERS.length];
+      mStickers[0] = new ObjectSticker(STICKERS[0],null,radii,stroke );
+      }
+
     return mStickers[face/NUM_FACES];
     }
 
@@ -281,7 +275,8 @@ class TwistyCube extends TwistyObject
 
   Static4D[] getQuats()
     {
-    return QUATS;
+    if( mQuats ==null ) initializeQuats();
+    return mQuats;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -326,7 +321,7 @@ class TwistyCube extends TwistyObject
 
   int getNumStickerTypes(int numLayers)
     {
-    return STICKERS.length;
+    return 1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -369,7 +364,8 @@ class TwistyCube extends TwistyObject
 
   public int[] getBasicAngle()
     {
-    return BASIC_ANGLE;
+    if( mBasicAngle ==null ) mBasicAngle = new int[] { 4,4,4 };
+    return mBasicAngle;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index b8e230da..079fc2cd 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -88,12 +88,13 @@ public abstract class TwistyObject extends DistortedNode
   private static final int POST_ROTATION_MILLISEC = 500;
 
   MeshBase[] mMeshes;
-  final Static4D[] QUATS;
+  final Static4D[] OBJECT_QUATS;
   final Cubit[] CUBITS;
   final int NUM_FACES;
   final int NUM_TEXTURES;
   final int NUM_CUBITS;
   final int NUM_AXIS;
+  final int NUM_QUATS;
 
   private final int mNumCubitFaces;
   private final Static3D[] mAxis;
@@ -158,11 +159,12 @@ public abstract class TwistyObject extends DistortedNode
     if( mCuts==null ) for(int i=0; i<mAxis.length; i++) mNumCuts[i] = 0;
     else              for(int i=0; i<mAxis.length; i++) mNumCuts[i] = mCuts[i].length;
 
-    QUATS = getQuats();
+    OBJECT_QUATS = getQuats();
     NUM_CUBITS  = mOrigPos.length;
     NUM_FACES = getNumFaces();
     NUM_TEXTURES = getNumStickerTypes(mNumLayers)*NUM_FACES;
     NUM_AXIS = mAxis.length;
+    NUM_QUATS = OBJECT_QUATS.length;
 
     boolean bandaged=false;
 
@@ -213,10 +215,9 @@ public abstract class TwistyObject extends DistortedNode
     mTexture = new DistortedTexture();
     mEffects = new DistortedEffects();
 
-    int num_quats = QUATS.length;
-    for(int q=0; q<num_quats; q++)
+    for(int q=0; q<NUM_QUATS; q++)
       {
-      VertexEffectQuaternion vq = new VertexEffectQuaternion(QUATS[q],CENTER);
+      VertexEffectQuaternion vq = new VertexEffectQuaternion(OBJECT_QUATS[q],CENTER);
       vq.setMeshAssociation(0,q);
       mEffects.apply(vq);
       }
@@ -343,7 +344,7 @@ public abstract class TwistyObject extends DistortedNode
 
   private void createDataStructuresForSolved(int numLayers)
     {
-    mTmpQuats = new int[QUATS.length];
+    mTmpQuats = new int[NUM_QUATS];
     mSolvedQuats = new int[NUM_CUBITS][];
 
     for(int c=0; c<NUM_CUBITS; c++)
@@ -432,11 +433,10 @@ public abstract class TwistyObject extends DistortedNode
     {
     if( mQuatMult==null )
       {
-      int len = QUATS.length;
-      mQuatMult = new int[len][len];
+      mQuatMult = new int[NUM_QUATS][NUM_QUATS];
 
-      for(int i=0; i<len; i++)
-        for(int j=0; j<len; j++) mQuatMult[i][j] = -1;
+      for(int i=0; i<NUM_QUATS; i++)
+        for(int j=0; j<NUM_QUATS; j++) mQuatMult[i][j] = -1;
       }
 
     if( mQuatMult[index1][index2]==-1 )
@@ -499,7 +499,7 @@ public abstract class TwistyObject extends DistortedNode
     float MAXDIFF = 0.01f;
     float[] center= mOrigPos[centerNum];
     Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
-    Static4D result = QuatHelper.rotateVectorByQuat(sc,QUATS[quatNum]);
+    Static4D result = QuatHelper.rotateVectorByQuat(sc,OBJECT_QUATS[quatNum]);
 
     float x = result.get0();
     float y = result.get1();
@@ -532,11 +532,10 @@ public abstract class TwistyObject extends DistortedNode
     {
     if( mScramble==null )
       {
-      int numQuats = QUATS.length;
-      mScramble = new int[numQuats][NUM_CUBITS];
+      mScramble = new int[NUM_QUATS][NUM_CUBITS];
       mColors   = new int[NUM_CUBITS];
 
-      for(int q=0; q<numQuats; q++)
+      for(int q=0; q<NUM_QUATS; q++)
         for(int c=0; c<NUM_CUBITS; c++) mScramble[q][c] = computeScramble(q,c);
       }
 
@@ -815,7 +814,7 @@ public abstract class TwistyObject extends DistortedNode
 
   int mulQuat(int q1, int q2)
     {
-    Static4D result = QuatHelper.quatMultiply(QUATS[q1],QUATS[q2]);
+    Static4D result = QuatHelper.quatMultiply(OBJECT_QUATS[q1],OBJECT_QUATS[q2]);
 
     float rX = result.get0();
     float rY = result.get1();
@@ -825,22 +824,22 @@ public abstract class TwistyObject extends DistortedNode
     final float MAX_ERROR = 0.1f;
     float dX,dY,dZ,dW;
 
-    for(int i=0; i<QUATS.length; i++)
+    for(int i=0; i<NUM_QUATS; i++)
       {
-      dX = QUATS[i].get0() - rX;
-      dY = QUATS[i].get1() - rY;
-      dZ = QUATS[i].get2() - rZ;
-      dW = QUATS[i].get3() - rW;
+      dX = OBJECT_QUATS[i].get0() - rX;
+      dY = OBJECT_QUATS[i].get1() - rY;
+      dZ = OBJECT_QUATS[i].get2() - rZ;
+      dW = OBJECT_QUATS[i].get3() - rW;
 
       if( dX<MAX_ERROR && dX>-MAX_ERROR &&
           dY<MAX_ERROR && dY>-MAX_ERROR &&
           dZ<MAX_ERROR && dZ>-MAX_ERROR &&
           dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
 
-      dX = QUATS[i].get0() + rX;
-      dY = QUATS[i].get1() + rY;
-      dZ = QUATS[i].get2() + rZ;
-      dW = QUATS[i].get3() + rW;
+      dX = OBJECT_QUATS[i].get0() + rX;
+      dY = OBJECT_QUATS[i].get1() + rY;
+      dZ = OBJECT_QUATS[i].get2() + rZ;
+      dW = OBJECT_QUATS[i].get3() + rW;
 
       if( dX<MAX_ERROR && dX>-MAX_ERROR &&
           dY<MAX_ERROR && dY>-MAX_ERROR &&
@@ -946,9 +945,9 @@ public abstract class TwistyObject extends DistortedNode
       {
       mQuatDebug[i] = CUBITS[i].restorePreferences(preferences);
 
-      if( mQuatDebug[i]>=0 && mQuatDebug[i]<QUATS.length)
+      if( mQuatDebug[i]>=0 && mQuatDebug[i]<NUM_QUATS)
         {
-        CUBITS[i].modifyCurrentPosition(QUATS[mQuatDebug[i]]);
+        CUBITS[i].modifyCurrentPosition(OBJECT_QUATS[mQuatDebug[i]]);
         mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),mQuatDebug[i]);
         }
       else
