commit 94ad5a8f6ba3779d55d34589e15ee43ba3d65e53
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 16 15:09:06 2020 +0000

    Fix the way we detect if an Object is solved.
    Previous way would not detect situations when the object looks solved, but one of the cubits inside its faces - a non-corner and non-edge - is rotated and ends up on the same face.

diff --git a/src/main/java/org/distorted/object/Cubit.java b/src/main/java/org/distorted/object/Cubit.java
index 98062564..445a9d3c 100644
--- a/src/main/java/org/distorted/object/Cubit.java
+++ b/src/main/java/org/distorted/object/Cubit.java
@@ -134,11 +134,11 @@ class Cubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void modifyCurrentPosition(Static3D currentPosition, Static4D quat)
+  private void modifyCurrentPosition(Static4D quat)
     {
-    float cubitCenterX = currentPosition.get0();
-    float cubitCenterY = currentPosition.get1();
-    float cubitCenterZ = currentPosition.get2();
+    float cubitCenterX = mCurrentPosition.get0();
+    float cubitCenterY = mCurrentPosition.get1();
+    float cubitCenterZ = mCurrentPosition.get2();
 
     Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
     Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
@@ -147,13 +147,12 @@ class Cubit
     float rotatedY = rotatedCenter.get1();
     float rotatedZ = rotatedCenter.get2();
 
-    currentPosition.set(rotatedX, rotatedY, rotatedZ);
-    mParent.clampPos(currentPosition);
+    mCurrentPosition.set(rotatedX, rotatedY, rotatedZ);
+    mParent.clampPos(mCurrentPosition);
 
     computeRotationRow();
     }
 
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // cast current position on axis; use mStart and mStep to compute the rotation row for each axis.
 
@@ -232,7 +231,78 @@ class Cubit
     float qw = preferences.getFloat("qw_"+number, 1.0f);
 
     mQuatScramble.set(qx,qy,qz,qw);
-    modifyCurrentPosition( mCurrentPosition, mQuatScramble);
+    modifyCurrentPosition(mQuatScramble);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
+// then if it were rotated by quaternion 'quat'.
+// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
+// middle squares get interchanged. No visible difference!
+//
+// So: this is true iff the cubit
+// a) is a corner or edge and the quaternions are the same
+// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
+
+  boolean thereIsNoVisibleDifference(Static4D quat)
+    {
+    if ( mQuatScramble.get0()==quat.get0() &&
+         mQuatScramble.get1()==quat.get1() &&
+         mQuatScramble.get2()==quat.get2() &&
+         mQuatScramble.get3()==quat.get3()  ) return true;
+
+    int belongsToHowManyFaces = 0;
+    int size = mParent.getSize()-1;
+    int row;
+
+    for(int i=0; i<mNumAxis; i++)
+      {
+      row = (int)(mRotationRow[i]+0.5f);
+      if( row==0 || row==size ) belongsToHowManyFaces++;
+      }
+
+    switch(belongsToHowManyFaces)
+      {
+      case 0 : return true ;  // 'inside' cubit that does not lie on any face
+      case 1 :                // cubit that lies inside one of the faces
+               float cubitCenterX = mCurrentPosition.get0();
+               float cubitCenterY = mCurrentPosition.get1();
+               float cubitCenterZ = mCurrentPosition.get2();
+
+               Static4D cubitCenter = new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
+               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
+               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, mQuatScramble );
+
+               int row1, row2;
+               float ax,ay,az;
+               Static3D axis;
+               float x1 = rotated1.get0();
+               float y1 = rotated1.get1();
+               float z1 = rotated1.get2();
+               float x2 = rotated2.get0();
+               float y2 = rotated2.get1();
+               float z2 = rotated2.get2();
+
+               for(int i=0; i<mNumAxis; i++)
+                 {
+                 axis = mParent.ROTATION_AXIS[i];
+                 ax = axis.get0();
+                 ay = axis.get1();
+                 az = axis.get2();
+
+                 row1 = (int)(( (x1*ax + y1*ay + z1*az) - mParent.mStart)/mParent.mStep + 0.5f);
+                 row2 = (int)(( (x2*ax + y2*ay + z2*az) - mParent.mStart)/mParent.mStep + 0.5f);
+
+                 if( (row1==0 && row2==0) || (row1==size && row2==size) )
+                   {
+                   return true;
+                   }
+                 }
+               return false;
+      default: return false;  // edge or corner
+
+
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -284,7 +354,7 @@ class Cubit
     mRotationAngle.removeAll();
     mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
     normalizeScrambleQuat( mQuatScramble );
-    modifyCurrentPosition( mCurrentPosition,quat);
+    modifyCurrentPosition(quat);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index ebf28a54..1babaa64 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -377,16 +377,9 @@ public abstract class RubikObject extends DistortedNode
     {
     Static4D q = mCubits[0].mQuatScramble;
 
-    float x = q.get0();
-    float y = q.get1();
-    float z = q.get2();
-    float w = q.get3();
-
-    for(int i=0; i<NUM_CUBITS; i++)
+    for(int i=1; i<NUM_CUBITS; i++)
       {
-      q = mCubits[i].mQuatScramble;
-
-      if( q.get0()!=x || q.get1()!=y || q.get2()!=z || q.get3()!=w ) return false;
+      if( !mCubits[i].thereIsNoVisibleDifference(q) ) return false;
       }
 
     return true;
@@ -407,8 +400,6 @@ public abstract class RubikObject extends DistortedNode
       return;
       }
 
-    //android.util.Log.e("object", "beginning new rotation: axis: "+axis+" row: "+row);
-
     mRotAxis = axis;
     mRotRow  = row;
 
