commit efef689c57ca96f65e3fe7b956607be383dbbba3
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Mar 5 15:45:15 2020 +0000

    Progress towards generalizing belongsToRotation()

diff --git a/src/main/java/org/distorted/effect/scramble/ScrambleEffect.java b/src/main/java/org/distorted/effect/scramble/ScrambleEffect.java
index 550821de..8edd2bae 100644
--- a/src/main/java/org/distorted/effect/scramble/ScrambleEffect.java
+++ b/src/main/java/org/distorted/effect/scramble/ScrambleEffect.java
@@ -23,7 +23,6 @@ import org.distorted.effect.BaseEffect;
 import org.distorted.library.effect.Effect;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.message.EffectListener;
-import org.distorted.library.type.Static3D;
 import org.distorted.magic.RubikRenderer;
 import org.distorted.object.RubikObject;
 
@@ -52,13 +51,6 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
   private static final int FAKE_EFFECT_ID  = -3;
   private static final Type[] types;
 
-  private static final Static3D VECTX = new Static3D(1,0,0);
-  private static final Static3D VECTY = new Static3D(0,1,0);
-  private static final Static3D VECTZ = new Static3D(0,0,1);
-  private static final Static3D VECTU = new Static3D(0,0,0);
-
-  private static final Static3D[] VECTORS = { VECTX, VECTY, VECTZ, VECTZ };
-
   static
     {
     int i=0;
@@ -156,7 +148,7 @@ public abstract class ScrambleEffect extends BaseEffect implements EffectListene
         android.util.Log.e("effect", "ERROR: "+mNumDoubleScramblesLeft);
         }
 
-      mCurrentBaseEffectID = mObject.addNewRotation(VECTORS[mLastVector], row, angle*90, durationMillis, this );
+      mCurrentBaseEffectID = mObject.addNewRotation(mLastVector, row, angle*90, durationMillis, this );
       }
     else
       {
diff --git a/src/main/java/org/distorted/magic/RubikSurfaceView.java b/src/main/java/org/distorted/magic/RubikSurfaceView.java
index ab6404de..31b0cc30 100644
--- a/src/main/java/org/distorted/magic/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/magic/RubikSurfaceView.java
@@ -265,10 +265,10 @@ public class RubikSurfaceView extends GLSurfaceView
                                            Static4D touchPoint2 = new Static4D(x, y, 0, 0);
                                            Static4D rotatedTouchPoint2= rotateVectorByInvertedQuat(touchPoint2, mQuatAccumulated);
 
-                                           Static4D rot = mMovement.newRotation(rotatedTouchPoint2);
+                                           Static2D rot = mMovement.newRotation(rotatedTouchPoint2);
                                            RubikObject object = mRenderer.getObject();
 
-                                           object.beginNewRotation( new Static3D(rot.get0(),rot.get1(),rot.get2()), (int)(object.getSize()*rot.get3()) );
+                                           object.beginNewRotation( (int)rot.get0(), (int)(object.getSize()*rot.get1()) );
 
                                            if( RubikState.getCurrentState()==RubikState.SOLV )
                                              {
diff --git a/src/main/java/org/distorted/object/Cubit.java b/src/main/java/org/distorted/object/Cubit.java
index 339d4785..d0d6c95f 100644
--- a/src/main/java/org/distorted/object/Cubit.java
+++ b/src/main/java/org/distorted/object/Cubit.java
@@ -231,18 +231,22 @@ class Cubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  Static4D returnRotationQuat( Static3D axis)
+  Static4D returnRotationQuat(int axis)
     {
     int pointNum = mRotationAngle.getNumPoints();
 
     if( pointNum>=1 )
       {
+      float axisX = mParent.ROTATION_AXIS[axis].get0();
+      float axisY = mParent.ROTATION_AXIS[axis].get1();
+      float axisZ = mParent.ROTATION_AXIS[axis].get2();
+
       float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
       int nearestAngleInDegrees = computeNearestAngle(startingAngle);
       double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
       float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
       float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
-      return new Static4D( axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
+      return new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
       }
 
     return null;
@@ -279,17 +283,17 @@ class Cubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void beginNewRotation(Static3D axis)
+  void beginNewRotation(int axis)
     {
-    mRotationAxis.set(axis);
+    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
     mRotationAngle.add(mParent.mRotationAngleStatic);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  void addNewRotation(Static3D axis, long durationMillis, int angle)
+  void addNewRotation(int axis, long durationMillis, int angle)
     {
-    mRotationAxis.set(axis);
+    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
     mRotationAngle.setDuration(durationMillis);
     mRotationAngle.resetToBeginning();
     mRotationAngle.add(new Static1D(0));
diff --git a/src/main/java/org/distorted/object/RubikCube.java b/src/main/java/org/distorted/object/RubikCube.java
index ce9e52fb..14af9baf 100644
--- a/src/main/java/org/distorted/object/RubikCube.java
+++ b/src/main/java/org/distorted/object/RubikCube.java
@@ -38,11 +38,40 @@ import org.distorted.library.type.Static4D;
 
 class RubikCube extends RubikObject
 {
-  private static final float SQ2 = 0.5f*((float)Math.sqrt(2));
-  private static final float[] LEGAL = new float[] { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 };
-
-  // axisXright (right-YELLOW) axisXleft (left-WHITE) axisYright (top-BLUE) axisYleft (bottom-GREEN) axisZright (front-RED) axisZleft (back-BROWN)
-  private static final int[] FACE_COLORS   = new int[] { 0xffffff00, 0xffffffff, 0xff0000ff, 0xff00ff00, 0xffff0000, 0xffb5651d };
+  // the three rotation axis of a RubikCube
+  private static final Static3D[] AXIS = new Static3D[]
+         {
+           new Static3D(1,0,0),
+           new Static3D(0,1,0),
+           new Static3D(0,0,1)
+         };
+
+  private static final int[] FACE_COLORS = new int[]
+         {
+           0xffffff00, 0xffffffff,   // AXIS[0]right (right-YELLOW) AXIS[0]left (left  -WHITE)
+           0xff0000ff, 0xff00ff00,   // AXIS[1]right (top  -BLUE  ) AXIS[1]left (bottom-GREEN)
+           0xffff0000, 0xffb5651d    // AXIS[2]right (front-RED   ) AXIS[2]left (back  -BROWN)
+         };
+
+  // All legal rotation quats of a RubikCube of any size must have all four of their components
+  // equal to either 0, +-1, +-0.5 or +-sqrt(2)/2.
+  // 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.
+  // 3) linear scan through those shows that the only floats in those 24 quats are those 7 given
+  // below.
+  private static final float[] LEGALQUATS = new float[]
+         {
+           0.0f ,
+           0.5f ,
+          -0.5f ,
+           1.0f ,
+          -1.0f ,
+           0.5f*((float)Math.sqrt(2)) ,
+          -0.5f*((float)Math.sqrt(2))
+         };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -86,12 +115,10 @@ class RubikCube extends RubikObject
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// All legal rotation quats of a RubikCube of any size must have all four of their components
-// equal to either 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
 
   float[] getLegalQuats()
     {
-    return LEGAL;
+    return LEGALQUATS;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -101,6 +128,13 @@ class RubikCube extends RubikObject
     return FACE_COLORS.length;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getRotationAxis()
+    {
+    return AXIS;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void createFaceTexture(Canvas canvas, Paint paint, int face)
@@ -151,48 +185,4 @@ class RubikCube extends RubikObject
 
     return new MeshJoined(meshes);
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean belongsToRotation( Static3D currentPosition, Static3D axis, int row)
-    {
-    if( axis.get0()!=0 ) return currentPosition.get0()==row;
-    if( axis.get1()!=0 ) return currentPosition.get1()==row;
-    if( axis.get2()!=0 ) return currentPosition.get2()==row;
-
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static final Static4D mapFront, mapBack, mapLeft, mapRight, mapTop, mapBottom, mapBlack;
-
-  static
-    {
-    // axisXright (right-YELLOW) axisXleft (left-WHITE) axisYright (top-BLUE) axisYleft (bottom-GREEN) axisZright (front-RED) axisZleft (back-BROWN)
-
-    mapRight = new Static4D( 0*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapLeft  = new Static4D( 1*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapTop   = new Static4D( 2*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapBottom= new Static4D( 3*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapFront = new Static4D( 4*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapBack  = new Static4D( 5*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    mapBlack = new Static4D( 6*(1/7.0f), 0.0f, 1/7.0f, 1.0f);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void textureCubitMesh(MeshBase mesh, int x, int y, int z)
-    {
-    Static4D tmpRight = (x== mSize-1 ? mapRight :mapBlack);
-    Static4D tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
-    Static4D tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
-    Static4D tmpBottom= (y==       0 ? mapBottom:mapBlack);
-    Static4D tmpFront = (z== mSize-1 ? mapFront :mapBlack);
-    Static4D tmpBack  = (z==       0 ? mapBack  :mapBlack);
-
-    Static4D[] maps = new Static4D[] { tmpRight, tmpLeft, tmpTop, tmpBottom, tmpFront, tmpBack};
-
-    mesh.setTextureMap(maps);
-    }
 }
diff --git a/src/main/java/org/distorted/object/RubikCubeMovement.java b/src/main/java/org/distorted/object/RubikCubeMovement.java
index 8b71d044..be09c97c 100644
--- a/src/main/java/org/distorted/object/RubikCubeMovement.java
+++ b/src/main/java/org/distorted/object/RubikCubeMovement.java
@@ -19,6 +19,7 @@
 
 package org.distorted.object;
 
+import org.distorted.library.type.Static2D;
 import org.distorted.library.type.Static4D;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -191,7 +192,7 @@ class RubikCubeMovement extends RubikObjectMovement
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public Static4D newRotation(Static4D rotatedTouchPoint)
+    public Static2D newRotation(Static4D rotatedTouchPoint)
       {
       float cubeHalfSize= RubikObject.OBJECT_SCREEN_RATIO*0.5f;
 
@@ -214,16 +215,7 @@ class RubikCubeMovement extends RubikObjectMovement
       mTouch[1] = mPoint[1];
       mTouch[2] = mPoint[2];
 
-      Static4D result=null;
-
-      switch(mRotationVect)
-        {
-        case VECTX: result = new Static4D(1,0,0,offset); break;
-        case VECTY: result = new Static4D(0,1,0,offset); break;
-        case VECTZ: result = new Static4D(0,0,1,offset); break;
-        }
-
-      return result;
+      return new Static2D(mRotationVect,offset);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index bbf6d9fe..693f5bd6 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -48,11 +48,12 @@ public abstract class RubikObject extends DistortedNode
   static final int TEXTURE_HEIGHT = 128;
   static final float OBJECT_SCREEN_RATIO = 0.5f;
   final float[] LEGAL_QUATS;
+  final Static3D[] ROTATION_AXIS;
 
   private static final int POST_ROTATION_MILLISEC = 500;
   private final int NUM_CUBITS;
   private int mRotRow;
-  private Static3D mRotAxis;
+  private int mRotAxis;
   private Static3D mScale, mNodeScale;
   private Static4D mQuatAccumulated;
   private Cubit[] mCubits;
@@ -77,6 +78,7 @@ public abstract class RubikObject extends DistortedNode
 
     LEGAL_QUATS = getLegalQuats();
     NUM_CUBITS  = getNumCubits(size);
+    ROTATION_AXIS = getRotationAxis();
 
     mSize = size;
 
@@ -108,18 +110,58 @@ public abstract class RubikObject extends DistortedNode
 
     for(int i=0; i<NUM_CUBITS; i++)
       {
-      int x = positions[i][0];
-      int y = positions[i][1];
-      int z = positions[i][2];
+      Static3D pos = new Static3D(positions[i][0],positions[i][1],positions[i][2]);
+      MeshBase cubitMesh = createCubitMesh(vertices);
+      mCubits[i] = new Cubit(this,cubitMesh,pos);
+      textureCubitMesh(cubitMesh,i);
 
-      MeshBase cubit = createCubitMesh(vertices);
-      textureCubitMesh(cubit,x,y,z);
-
-      mCubits[i] = new Cubit( this , cubit, new Static3D(x,y,z) );
       attach(mCubits[i].mNode);
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void textureCubitMesh(MeshBase mesh, int cubit)
+    {
+    boolean belongs;
+    final int numFaces = getNumFaces();
+    final Static4D[] maps = new Static4D[numFaces];
+    final float ratio = 1.0f/(numFaces+1);
+
+    if( 2*ROTATION_AXIS.length == numFaces )
+      {
+      for(int i=0; i<numFaces; i++)
+        {
+        belongs = belongsToRotation(cubit, i/2, i%2==0 ? mSize-1:0 );
+        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
+        }
+      }
+    else if( ROTATION_AXIS.length == numFaces )
+      {
+      for(int i=0; i<numFaces; i++)
+        {
+        belongs = belongsToRotation(cubit, i, mSize-1 );
+        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
+        }
+      }
+
+    mesh.setTextureMap(maps);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO: only works for RubikCube
+
+  private boolean belongsToRotation( int cubit, int axis, int row)
+    {
+    Static3D position = mCubits[cubit].mCurrentPosition;
+
+    if( axis==0 ) return position.get0()==row;
+    if( axis==1 ) return position.get1()==row;
+    if( axis==2 ) return position.get2()==row;
+
+    return false;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void resetRotationAngle(Dynamic1D rotationAngle)
@@ -229,7 +271,7 @@ public abstract class RubikObject extends DistortedNode
 
     for(int i=0; i<NUM_CUBITS; i++)
       {
-      if( belongsToRotation(mCubits[i].mCurrentPosition,mRotAxis,mRotRow) )
+      if( belongsToRotation(i,mRotAxis,mRotRow) )
         {
         if( first )
           {
@@ -296,7 +338,7 @@ public abstract class RubikObject extends DistortedNode
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void beginNewRotation(Static3D axis, int row )
+  public void beginNewRotation(int axis, int row )
     {
     mRotAxis = axis;
     mRotRow  = row;
@@ -304,7 +346,7 @@ public abstract class RubikObject extends DistortedNode
     mRotationAngleStatic.set0(0.0f);
 
     for(int i=0; i<NUM_CUBITS; i++)
-      if( belongsToRotation( mCubits[i].mCurrentPosition,axis,mRotRow) )
+      if( belongsToRotation(i,mRotAxis,mRotRow) )
         {
         mCubits[i].beginNewRotation(axis);
         }
@@ -312,7 +354,7 @@ public abstract class RubikObject extends DistortedNode
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public long addNewRotation( Static3D axis, int row, int angle, long durationMillis, EffectListener listener )
+  public long addNewRotation( int axis, int row, int angle, long durationMillis, EffectListener listener )
      {
      long effectID=0;
      boolean first = true;
@@ -323,7 +365,7 @@ public abstract class RubikObject extends DistortedNode
      mRotationAngleStatic.set0(0.0f);
 
      for(int i=0; i<NUM_CUBITS; i++)
-       if( belongsToRotation(mCubits[i].mCurrentPosition,axis,mRotRow) )
+       if( belongsToRotation(i,mRotAxis,mRotRow) )
          {
          mCubits[i].addNewRotation(axis,durationMillis,angle);
 
@@ -345,7 +387,7 @@ public abstract class RubikObject extends DistortedNode
      Static4D quat = null;
 
      for(int i=0; i<NUM_CUBITS; i++)
-       if( belongsToRotation(mCubits[i].mCurrentPosition,mRotAxis,mRotRow) )
+       if( belongsToRotation(i,mRotAxis,mRotRow) )
          {
          if( first )
            {
@@ -367,7 +409,5 @@ public abstract class RubikObject extends DistortedNode
   abstract int getNumFaces();
   abstract void createFaceTexture(Canvas canvas, Paint paint, int face);
   abstract MeshBase createCubitMesh(int vertices);
-
-  abstract boolean belongsToRotation(Static3D position, Static3D axis, int row);
-  abstract void textureCubitMesh(MeshBase mesh, int x, int y, int z);
+  abstract Static3D[] getRotationAxis();
   }
diff --git a/src/main/java/org/distorted/object/RubikObjectMovement.java b/src/main/java/org/distorted/object/RubikObjectMovement.java
index 5801823b..487f2658 100644
--- a/src/main/java/org/distorted/object/RubikObjectMovement.java
+++ b/src/main/java/org/distorted/object/RubikObjectMovement.java
@@ -19,6 +19,7 @@
 
 package org.distorted.object;
 
+import org.distorted.library.type.Static2D;
 import org.distorted.library.type.Static4D;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -26,6 +27,6 @@ import org.distorted.library.type.Static4D;
 public abstract class RubikObjectMovement
   {
   public abstract boolean faceTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera);
-  public abstract Static4D newRotation(Static4D rotatedTouchPoint);
+  public abstract Static2D newRotation(Static4D rotatedTouchPoint);
   public abstract float continueRotation(Static4D rotatedTouchPoint);
   }
