commit 1ebc47679b099212c89c85ad0d607e219fb344dd
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Aug 19 22:10:38 2020 +0100

    Fix detecting if an Object is solved. Before, the generic Cubit.thereIsNoVisibleDifference(0 would not work correctly in case of the Dino.

diff --git a/src/main/java/org/distorted/objects/Cubit.java b/src/main/java/org/distorted/objects/Cubit.java
index 5aba6282..f3bbeada 100644
--- a/src/main/java/org/distorted/objects/Cubit.java
+++ b/src/main/java/org/distorted/objects/Cubit.java
@@ -150,6 +150,13 @@ class Cubit
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D getOrigPosition()
+    {
+    return mOrigPosition;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int computeAssociation()
@@ -185,81 +192,6 @@ class Cubit
     return mQuatIndex;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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(int quatIndex)
-    {
-    if ( mQuatIndex == quatIndex ) return true;
-
-    int belongsToHowManyFaces = 0;
-    int size = mParent.getSize()-1;
-    float row;
-    final float MAX_ERROR = 0.01f;
-
-    for(int i=0; i<mNumAxis; i++)
-      {
-      row = mRotationRow[i];
-      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
-          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) 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 = mOrigPosition.get0();
-               float cubitCenterY = mOrigPosition.get1();
-               float cubitCenterZ = mOrigPosition.get2();
-
-               Static4D quat1 = mParent.QUATS[quatIndex];
-               Static4D quat2 = mParent.QUATS[mQuatIndex];
-
-               Static4D cubitCenter = new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
-               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
-               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
-
-               float row1, row2, row3, row4;
-               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 = ((x1*ax + y1*ay + z1*az) - mParent.mStart) / mParent.mStep;
-                 row2 = ((x2*ax + y2*ay + z2*az) - mParent.mStart) / mParent.mStep;
-                 row3 = row1 - size;
-                 row4 = row2 - size;
-
-                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
-                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
-                   {
-                   return true;
-                   }
-                 }
-               return false;
-
-      default: return false;  // edge or corner
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int removeRotationNow(Static4D quat)
diff --git a/src/main/java/org/distorted/objects/RubikCube.java b/src/main/java/org/distorted/objects/RubikCube.java
index ea86bff2..7f1bc387 100644
--- a/src/main/java/org/distorted/objects/RubikCube.java
+++ b/src/main/java/org/distorted/objects/RubikCube.java
@@ -35,6 +35,7 @@ import org.distorted.library.mesh.MeshRectangles;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
+import org.distorted.main.RubikSurfaceView;
 
 import java.util.Random;
 
@@ -393,6 +394,78 @@ class RubikCube extends RubikObject
     return 0;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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(Cubit cubit, int quatIndex)
+    {
+    if ( cubit.mQuatIndex == quatIndex ) return true;
+
+    int belongsToHowManyFaces = 0;
+    int size = getSize()-1;
+    float row;
+    final float MAX_ERROR = 0.01f;
+
+    for(int i=0; i<NUM_AXIS; i++)
+      {
+      row = cubit.mRotationRow[i];
+      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
+          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) 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
+               Static3D orig = cubit.getOrigPosition();
+               Static4D quat1 = QUATS[quatIndex];
+               Static4D quat2 = QUATS[cubit.mQuatIndex];
+
+               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
+               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
+               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
+
+               float row1, row2, row3, row4;
+               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<NUM_AXIS; i++)
+                 {
+                 axis = ROTATION_AXIS[i];
+                 ax = axis.get0();
+                 ay = axis.get1();
+                 az = axis.get2();
+
+                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
+                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
+                 row3 = row1 - size;
+                 row4 = row2 - size;
+
+                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
+                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
+                   {
+                   return true;
+                   }
+                 }
+               return false;
+
+      default: return false;  // edge or corner
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // order: Up --> Right --> Front --> Down --> Left --> Back
 // (because the first implemented Solver - the two-phase Cube3 one - expects such order)
diff --git a/src/main/java/org/distorted/objects/RubikDino.java b/src/main/java/org/distorted/objects/RubikDino.java
index 755c4514..e126e794 100644
--- a/src/main/java/org/distorted/objects/RubikDino.java
+++ b/src/main/java/org/distorted/objects/RubikDino.java
@@ -441,6 +441,15 @@ public class RubikDino extends RubikObject
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// here it's simple - all cubits have to be rotated with the same quaretnion for the whole thing
+// to be solved.
+
+  boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
+    {
+    return cubit.mQuatIndex == quatIndex;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // TODO  (only needed for solvers - there are no Dino solvers ATM)
 
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index cbbae876..f12c6e25 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -70,6 +70,7 @@ public abstract class RubikObject extends DistortedNode
   final Static4D[] QUATS;
   final int NUM_FACES;
   final int NUM_CUBIT_FACES;
+  final int NUM_AXIS;
 
   private static float mInitScreenRatio,mObjectScreenRatio;
 
@@ -116,6 +117,7 @@ public abstract class RubikObject extends DistortedNode
     QUATS = getQuats();
     NUM_CUBITS  = mOrigPos.length;
     ROTATION_AXIS = getRotationAxis();
+    NUM_AXIS = ROTATION_AXIS.length;
     mObjectScreenRatio = getScreenRatio();
     mInitScreenRatio = mObjectScreenRatio;
     NUM_FACES = getNumFaces();
@@ -485,7 +487,7 @@ public abstract class RubikObject extends DistortedNode
 
     for(int i=1; i<NUM_CUBITS; i++)
       {
-      if( !mCubits[i].thereIsNoVisibleDifference(index) ) return false;
+      if( !thereIsNoVisibleDifference(mCubits[i], index) ) return false;
       }
 
     return true;
@@ -701,6 +703,7 @@ public abstract class RubikObject extends DistortedNode
   abstract int getFaceColor(int cubit, int cubitface, int size);
   abstract float returnMultiplier();
   abstract float[] getRowChances();
+  abstract boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex);
 
   public abstract Static3D[] getRotationAxis();
   public abstract int getBasicAngle();
diff --git a/src/main/java/org/distorted/objects/RubikPyraminx.java b/src/main/java/org/distorted/objects/RubikPyraminx.java
index 515ca126..de25f5e6 100644
--- a/src/main/java/org/distorted/objects/RubikPyraminx.java
+++ b/src/main/java/org/distorted/objects/RubikPyraminx.java
@@ -37,6 +37,7 @@ import org.distorted.library.mesh.MeshTriangles;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
+import org.distorted.main.RubikSurfaceView;
 
 import java.util.Random;
 
@@ -485,6 +486,78 @@ public class RubikPyraminx extends RubikObject
     return 0;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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(Cubit cubit, int quatIndex)
+    {
+    if ( cubit.mQuatIndex == quatIndex ) return true;
+
+    int belongsToHowManyFaces = 0;
+    int size = getSize()-1;
+    float row;
+    final float MAX_ERROR = 0.01f;
+
+    for(int i=0; i<NUM_AXIS; i++)
+      {
+      row = cubit.mRotationRow[i];
+      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
+          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) 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
+               Static3D orig = cubit.getOrigPosition();
+               Static4D quat1 = QUATS[quatIndex];
+               Static4D quat2 = QUATS[cubit.mQuatIndex];
+
+               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
+               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
+               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
+
+               float row1, row2, row3, row4;
+               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<NUM_AXIS; i++)
+                 {
+                 axis = ROTATION_AXIS[i];
+                 ax = axis.get0();
+                 ay = axis.get1();
+                 az = axis.get2();
+
+                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
+                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
+                 row3 = row1 - size;
+                 row4 = row2 - size;
+
+                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
+                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
+                   {
+                   return true;
+                   }
+                 }
+               return false;
+
+      default: return false;  // edge or corner
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // TODO  (only needed for solvers - there are no Pyraminx solvers ATM)
 
