commit 8be29dfc91b79fb3e81409aa969b182ac461c8ef
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat Apr 6 00:52:50 2019 +0100

    Improve the Rubik App - beginnings of rotation.

diff --git a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
index 550207d..4a6f032 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikRenderer.java
@@ -24,17 +24,17 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.opengl.GLSurfaceView;
 
-import org.distorted.library.effect.EffectName;
 import org.distorted.library.effect.MatrixEffectMove;
 import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.effect.MatrixEffectRotate;
 import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.effect.PostprocessEffectGlow;
 import org.distorted.library.effect.VertexEffectSink;
 import org.distorted.library.main.Distorted;
 import org.distorted.library.main.DistortedEffects;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshCubes;
+import org.distorted.library.type.Dynamic1D;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
@@ -48,21 +48,26 @@ class RubikRenderer implements GLSurfaceView.Renderer
 {
             static final int NUM_CUBES =   3;
     private static final int SIZE      = 200;
-
     private static final float CUBE_SCREEN_RATIO = 0.5f;
 
+    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 GLSurfaceView mView;
     private DistortedTexture mTexture;
     private DistortedScreen mScreen;
     private Static3D mMove, mScale, mCenter;
     private MeshCubes[][][] mCubes;
     private DistortedEffects[][][] mEffects;
+    private Static4D[][][] mQuatScramble;
+    private Static3D[][][] mRotationAxis;
+    private Dynamic1D[][][] mRotationAngle;
+    private Static1D mRotationAngleStatic;
+    private int mRotAxis;
+    private int mRotRow;
 
     private int mScreenWidth, mScreenHeight;
-    private int mLastRow, mLastCol, mLastSli;
-
-    private Static1D mGlowRadius;
-    private Static4D mGlowColor;
 
     private Static4D mQuatCurrent, mQuatAccumulated;
     private Static4D mTempCurrent, mTempAccumulated;
@@ -86,6 +91,10 @@ class RubikRenderer implements GLSurfaceView.Renderer
       mEffects = new DistortedEffects[NUM_CUBES][NUM_CUBES][NUM_CUBES];
       Static3D[][][] cubeVectors = new Static3D[NUM_CUBES][NUM_CUBES][NUM_CUBES];
 
+      mQuatScramble = new Static4D[NUM_CUBES][NUM_CUBES][NUM_CUBES];
+      mRotationAxis = new Static3D[NUM_CUBES][NUM_CUBES][NUM_CUBES];
+      mRotationAngle= new Dynamic1D[NUM_CUBES][NUM_CUBES][NUM_CUBES];
+
       float sinkDegree = 3.0f - 1.7f/NUM_CUBES; // f(1)=1.3, f(inf)=3
 
       VertexEffectSink sink = new VertexEffectSink( new Static1D(sinkDegree),
@@ -95,10 +104,8 @@ class RubikRenderer implements GLSurfaceView.Renderer
       mScale = new Static3D(1,1,1);
       mCenter= new Static3D(0,0,0);
 
-      mLastRow = mLastCol = mLastSli = 0;
-
-      mGlowRadius = new Static1D(5);
-      mGlowColor  = new Static4D(1.0f,1.0f,1.0f,0.6f);
+      mRotationAngleStatic = new Static1D(0);
+      mRotAxis= RubikSurfaceView.VECTN;
 
       MatrixEffectMove       move  = new MatrixEffectMove(mMove);
       MatrixEffectScale      scale = new MatrixEffectScale(mScale);
@@ -143,8 +150,10 @@ class RubikRenderer implements GLSurfaceView.Renderer
               tmpBottom= (y==            0 ? mapBottom:mapBlack);
 
               mCubes[x][y][z] = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
-
               cubeVectors[x][y][z] = new Static3D( SIZE*(x-nc), SIZE*(y-nc), SIZE*(z-nc) );
+              mQuatScramble[x][y][z] = new Static4D(0,0,0,1);
+              mRotationAngle[x][y][z] = new Dynamic1D();
+              mRotationAxis[x][y][z] = new Static3D(1,0,0);
 
               mEffects[x][y][z] = new DistortedEffects();
               mEffects[x][y][z].apply(sink);
@@ -152,6 +161,8 @@ class RubikRenderer implements GLSurfaceView.Renderer
               mEffects[x][y][z].apply(scale);
               mEffects[x][y][z].apply(quat1);
               mEffects[x][y][z].apply(quat2);
+              mEffects[x][y][z].apply( new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], mCenter));
+              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], mCenter));
               mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
               }
             }
@@ -242,7 +253,6 @@ class RubikRenderer implements GLSurfaceView.Renderer
               mScreen.attach(mTexture,mEffects[x][y][z],mCubes[x][y][z]);
               }
 
-      PostprocessEffectGlow.enable();
       VertexEffectSink.enable();
 
       try
@@ -283,62 +293,103 @@ class RubikRenderer implements GLSurfaceView.Renderer
 
     void addNewRotation(int vector, int row )
       {
-      String vect="?";
+      Static3D axis = VectX;
 
       switch(vector)
         {
-        case RubikSurfaceView.VECTX: vect="X"; break;
-        case RubikSurfaceView.VECTY: vect="Y"; break;
-        case RubikSurfaceView.VECTZ: vect="Z"; break;
+        case RubikSurfaceView.VECTX: axis = VectX; break;
+        case RubikSurfaceView.VECTY: axis = VectY; break;
+        case RubikSurfaceView.VECTZ: axis = VectZ; break;
         }
 
-      android.util.Log.e("rubik", "added new rotation (row="+row+" vector: "+vect+")");
+      mRotAxis = vector;
+      mRotRow  = row;
+
+      mRotationAngleStatic.set1(0.0f);
+
+      for(int x = 0; x< NUM_CUBES; x++)
+        for(int y = 0; y< NUM_CUBES; y++)
+          for(int z = 0; z< NUM_CUBES; z++)
+            if( x==0 || x==NUM_CUBES-1 || y==0 || y==NUM_CUBES-1 || z==0 || z==NUM_CUBES-1 )
+              {
+              if( belongsToRotation(x,y,z,vector,row) )
+                {
+                mRotationAxis[x][y][z].set(axis);
+                mRotationAngle[x][y][z].add(mRotationAngleStatic);
+                }
+              }
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     void continueRotation(float angle)
       {
-      android.util.Log.e("rubik", "rotating by angle: "+ (((float)((int)(angle*100.0f)))/100.0f) );
+      mRotationAngleStatic.set1(120.0f*angle/mScreenMin);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     void finishRotation()
       {
-      android.util.Log.e("rubik", "finishing rotation");
-      }
+      float nearestAngle = (mRotationAngleStatic.get1()+45.0f)/90.0f;
+      if( nearestAngle<0 ) nearestAngle-=1.0f;
+      int nearestAngleInDegrees = 90*(4-((int)nearestAngle+4)%4);
+      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
+      float sinA = (float)Math.sin(nearestAngleInRadians*0.5);
+      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+      mRotationAngleStatic.set1(0);
 
-    float getScreenWidth()
-      {
-      return mScreenWidth;
+      float qx=0,qy=0,qz=0;
+
+      switch(mRotAxis)
+        {
+        case RubikSurfaceView.VECTX: qx=1; break;
+        case RubikSurfaceView.VECTY: qy=1; break;
+        case RubikSurfaceView.VECTZ: qz=1; break;
+        }
+
+      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
+
+      for(int x = 0; x< NUM_CUBES; x++)
+        for(int y = 0; y< NUM_CUBES; y++)
+          for(int z = 0; z< NUM_CUBES; z++)
+            if( x==0 || x==NUM_CUBES-1 || y==0 || y==NUM_CUBES-1 || z==0 || z==NUM_CUBES-1 )
+              {
+              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
+                {
+                mRotationAngle[x][y][z].removeAll();
+                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
+                }
+              }
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    float getScreenHeight()
+    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
       {
-      return mScreenHeight;
+      switch(vector)
+        {
+        case RubikSurfaceView.VECTX: return x==row;
+        case RubikSurfaceView.VECTY: return y==row;
+        case RubikSurfaceView.VECTZ: return z==row;
+        }
+
+      return false;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void abortLastEffect()
+    float getScreenWidth()
       {
-      mEffects[mLastCol][mLastRow][mLastSli].abortByName(EffectName.GLOW);
+      return mScreenWidth;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void applyNewEffect(int col, int row, int sli)
+    float getScreenHeight()
       {
-      mLastCol = col;
-      mLastRow = row;
-      mLastSli = sli;
-
-      mEffects[mLastCol][mLastRow][mLastSli].apply(new PostprocessEffectGlow(mGlowRadius,mGlowColor));
+      return mScreenHeight;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java b/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
index 70897c2..1c7de11 100644
--- a/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
@@ -51,7 +51,6 @@ class RubikSurfaceView extends GLSurfaceView
 
     private boolean mDragging, mRotating;
     private int mX, mY;
-    private int mTouchedRow, mTouchedCol, mTouchedSli;
     private Static4D mQuatCurrent, mQuatAccumulated;
     private int mRotationVect;
     private RubikRenderer mRenderer;
@@ -106,8 +105,6 @@ class RubikSurfaceView extends GLSurfaceView
 
                                        if( mLastTouchedFace != NONE )
                                          {
-                                         mRenderer.abortLastEffect();
-                                         mRenderer.applyNewEffect(mTouchedCol, mTouchedRow, mTouchedSli);
                                          mRotating = true;
                                          }
                                        else
@@ -127,6 +124,8 @@ class RubikSurfaceView extends GLSurfaceView
                                          if( (mX-x)*(mX-x)+(mY-y)*(mY-y)>minimumToRotate )
                                            {
                                            addNewRotation(x,y);
+                                           mX = x;
+                                           mY = y;
                                            mRotating = false;
                                            }
                                          }
@@ -207,7 +206,7 @@ class RubikSurfaceView extends GLSurfaceView
       {
       fillTouchPoint(x,y);
 
-      float angle=0.0f;
+      int sign=1;
       float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
       float A=retA(mLastTouchedFace,cubeHalfSize);
 
@@ -219,32 +218,36 @@ class RubikSurfaceView extends GLSurfaceView
         {
         case VECTX: switch(mLastTouchedFace)
                       {
-                      case FRONT : angle = returnAngle(-diffZ, diffY); break;
-                      case BACK  : angle = returnAngle( diffZ,-diffY); break;
-                      case TOP   : angle = returnAngle(-diffY,-diffZ); break;
-                      case BOTTOM: angle = returnAngle( diffY, diffZ); break;
+                      case FRONT : sign = returnSign(-diffY); break;
+                      case BACK  : sign = returnSign( diffY); break;
+                      case TOP   : sign = returnSign( diffZ); break;
+                      case BOTTOM: sign = returnSign(-diffZ); break;
                       }
                     break;
         case VECTY: switch(mLastTouchedFace)
                       {
-                      case FRONT : angle = returnAngle(-diffZ, diffX); break;
-                      case BACK  : angle = returnAngle( diffZ,-diffX); break;
-                      case LEFT  : angle = returnAngle( diffX, diffZ); break;
-                      case RIGHT : angle = returnAngle(-diffX,-diffZ); break;
+                      case FRONT : sign = returnSign( diffX); break;
+                      case BACK  : sign = returnSign(-diffX); break;
+                      case LEFT  : sign = returnSign( diffZ); break;
+                      case RIGHT : sign = returnSign(-diffZ); break;
                       }
                     break;
         case VECTZ: switch(mLastTouchedFace)
                       {
-                      case TOP   : angle = returnAngle( diffY, diffX); break;
-                      case BOTTOM: angle = returnAngle(-diffY,-diffX); break;
-                      case LEFT  : angle = returnAngle(-diffX, diffY); break;
-                      case RIGHT : angle = returnAngle( diffX,-diffY); break;
+                      case TOP   : sign = returnSign(-diffX); break;
+                      case BOTTOM: sign = returnSign( diffX); break;
+                      case LEFT  : sign = returnSign(-diffY); break;
+                      case RIGHT : sign = returnSign( diffY); break;
                       }
                     break;
         default   : android.util.Log.e("View", "impossible vector: "+mRotationVect);
         }
 
-      mRenderer.continueRotation(angle);
+      float dX = mX-x;
+      float dY = mY-y;
+      float calibration = (float)Math.sqrt(dX*dX+dY*dY);
+
+      mRenderer.continueRotation(calibration*sign);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -258,12 +261,9 @@ class RubikSurfaceView extends GLSurfaceView
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private float returnAngle(float diff1, float diff2)
+    private int returnSign(float diff)
       {
-      float len = (float)Math.sqrt(diff1*diff1 + diff2*diff2);
-      len /= mRenderer.mScreenMin;
-
-      return diff2>0 ? len : -len;
+      return diff>0 ? 1 : -1;
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -277,7 +277,7 @@ class RubikSurfaceView extends GLSurfaceView
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // return quat1*quat2
 
-    private Static4D quatMultiply( Static4D quat1, Static4D quat2 )
+    static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
       {
       float qx = quat1.get1();
       float qy = quat1.get2();
@@ -403,24 +403,10 @@ class RubikSurfaceView extends GLSurfaceView
           float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
           float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
 
-          if( qX==1.0f ) qX-= 0.5f/RubikRenderer.NUM_CUBES;
-          if( qY==1.0f ) qY-= 0.5f/RubikRenderer.NUM_CUBES;
-          if( qZ==1.0f ) qZ-= 0.5f/RubikRenderer.NUM_CUBES;
-
-          if( qX<1 && qX>=0 && qY<1 && qY>=0 && qZ<1 && qZ>=0 )
-            {
-            mTouchedCol = (int)(qX*RubikRenderer.NUM_CUBES);
-            mTouchedRow = (int)(qY*RubikRenderer.NUM_CUBES);
-            mTouchedSli = (int)(qZ*RubikRenderer.NUM_CUBES);
-            return face;
-            }
+          if( qX<=1 && qX>=0 && qY<=1 && qY>=0 && qZ<=1 && qZ>=0 ) return face;
           }
         }
 
-      mTouchedRow = -1;
-      mTouchedCol = -1;
-      mTouchedSli = -1;
-
       return NONE;
       }
 
