commit bdfb04b3a438107a53e9455cdc784d5593e0fb44
Author: leszek <leszek@koltunski.pl>
Date:   Mon Sep 30 16:23:40 2024 +0200

    Further abstractions in the 'detect solved state' engine.
    
    It should now be able to work in all cases - but it is still buggy (Dino4 does not work now!)

diff --git a/src/main/java/org/distorted/objectlib/main/TwistyObject.java b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
index 8e938d2c..a05daddd 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyObject.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyObject.java
@@ -1752,6 +1752,53 @@ public abstract class TwistyObject
     return mObjectQuats;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Return the 'surface' the given face of the given cubit is on. (px,py,pz) is the pre-computed
+// center of the cubit (so we don't have to keep re-computing this per each face)
+// If the face is not external, return null.
+//
+// This is needed by the TwistyObjectSolved's method2 detecting if the object is in a solved state.
+//
+// What is a 'surface' ? For objects with flat monochromatic external walls, it is simply a 4-tuple
+// [(nx,ny,nz),d] (normal vector + distance from origin) describing a 3D plane this face is part of.
+// Exceptions are objects with curved external walls (ATM Masterball & Penrose Cubes): it is a union
+// of several surfaces which together define the bent, monochromatic wall. I.e. Penrose Cube has 3
+// surfaces (blue, yellow and red), each defined by 3 planes (think before the bending effect!)
+//
+// This is the default which simply returns the single plane. If some puzzle has more complicated
+// surfaces, it needs to override this method.
+//
+// We never need to test if a given point is part of a given surface (see TwistyObjectSurface!) - we
+// only need to be able to rotate the surface by quats and test if two surfaces are the same. Thus:
+// it is possible to provide some artificial surfaces - for example, in case of the Penrose, the
+// three surfaces can be only the single 'middle' plane [the one on the bend].
+
+  public float[] getMonochromaticSurface(int variant, int cubitIndex, int faceIndex, float px, float py, float pz)
+    {
+    if( !faceIsOuter(cubitIndex,faceIndex) ) return null;
+
+    Static4D cubitQuat = getCubitQuats(cubitIndex,mNumLayers);
+
+    float[] normal   = new float[4];
+    float[] intPoint = new float[3];
+    float[] rotPoint = new float[4];
+    float[] surface  = new float[4];
+
+    ObjectShape objShape = mShapes[variant];
+    objShape.getFacePoint(faceIndex,intPoint);
+    objShape.getFaceNormal(faceIndex,normal);
+
+    QuatHelper.rotateVectorByQuat(rotPoint,intPoint[0],intPoint[1],intPoint[2],1,cubitQuat);
+    QuatHelper.rotateVectorByQuat(surface,normal[0],normal[1],normal[2],0,cubitQuat);
+
+    float x = rotPoint[0] + px;
+    float y = rotPoint[1] + py;
+    float z = rotPoint[2] + pz;
+    surface[3] = x*surface[0] + y*surface[1] + z*surface[2];
+
+    return surface;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int[][] getVariantFaceIsOuter()
diff --git a/src/main/java/org/distorted/objectlib/main/TwistyObjectSolved.java b/src/main/java/org/distorted/objectlib/main/TwistyObjectSolved.java
index 5bc4152b..f1bc1825 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyObjectSolved.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyObjectSolved.java
@@ -388,14 +388,12 @@ class TwistyObjectSolved
     for(int c=0; c<numCubits; c++)
       {
       int variant = mParent.getCubitVariant(c,numLayers);
-      ObjectShape s = shapes[variant];
-      int numFaces = s.getNumFaces();
+      ObjectShape objShape = shapes[variant];
+      int numFaces = objShape.getNumFaces();
       mCubitFaceToPuzzleFaceMap[c] = new int[numFaces];
       mCubitFaceToSurfaceMap[c] = new int[numFaces];
       for(int f=0; f<numFaces; f++) mCubitFaceToSurfaceMap[c][f] = -1;
 
-      Static4D cubitQuat = mParent.getCubitQuats(c,numLayers);
-
       float[] po = pos[c];
       int poslen = po.length/3;
       float px=0, py=0, pz=0;
@@ -417,24 +415,9 @@ class TwistyObjectSolved
 
         //android.util.Log.e("D", "cubit "+c+" face "+f+" puzzle face: "+ mCubitFaceToPuzzleFaceMap[c][f]);
 
-        if( !mParent.faceIsOuter(c,f) ) continue;
-
-        float[] surface  = new float[4];
-        float[] rotPoint = new float[4];
-        float[] normal   = new float[4];
-        float[] intPoint = new float[3];
-
-        s.getFacePoint(f,intPoint);
-        s.getFaceNormal(f,normal);
-
-        QuatHelper.rotateVectorByQuat(rotPoint,intPoint[0],intPoint[1],intPoint[2],1,cubitQuat);
-        QuatHelper.rotateVectorByQuat(surface,normal[0],normal[1],normal[2],0,cubitQuat);
-
-        float x = rotPoint[0] + px;
-        float y = rotPoint[1] + py;
-        float z = rotPoint[2] + pz;
-        surface[3] = x*surface[0] + y*surface[1] + z*surface[2];
+        float[] surface = mParent.getMonochromaticSurface(variant,c,f,px,py,pz);
 
+        if( surface==null ) continue;
         TwistyObjectSurface si = new TwistyObjectSurface(surface);
         int index = surfaceExists(tmpSurfaces,si);
 
diff --git a/src/main/java/org/distorted/objectlib/main/TwistyObjectSurface.java b/src/main/java/org/distorted/objectlib/main/TwistyObjectSurface.java
index edc4b90e..3f0bfcf8 100644
--- a/src/main/java/org/distorted/objectlib/main/TwistyObjectSurface.java
+++ b/src/main/java/org/distorted/objectlib/main/TwistyObjectSurface.java
@@ -21,7 +21,7 @@ import org.distorted.library.type.Static4D;
 // monochromatic surfaces (Masterball, Penroses) cannot use this method..
 //
 // 'indices' is a map which shows which surface our surface changes to when rotated by all the
-// 'numQuats' quats.
+// 'numQuats' quats. Only kept here for a moment, then offloaded to TwistyObjectSolved.mSurfaceTable
 //
 // Used only in TwistyObjectSolved's method2.
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,41 +56,99 @@ class TwistyObjectSurface
 
   String print()
     {
-    return surface[0]+" "+surface[1]+" "+surface[2]+" "+surface[3];
+    int numPlanes = surface.length / 4;
+    StringBuilder sb = new StringBuilder();
+
+    for(int p=0; p<numPlanes; p++)
+      {
+      sb.append('[');
+      sb.append(surface[4*p]);
+      sb.append(' ');
+      sb.append(surface[4*p+1]);
+      sb.append(' ');
+      sb.append(surface[4*p+2]);
+      sb.append(' ');
+      sb.append(surface[4*p+3]);
+      sb.append(']');
+      sb.append(' ');
+      }
+
+    return sb.toString();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  boolean isSame(TwistyObjectSurface s)
+  private boolean surfaceSame(float[] s1, int i1, float[] s2, int i2)
     {
     final float MAX_ERROR = 0.01f;
     float dnx,dny,dnz,dnw;
-    float[] sur = s.surface;
-
-    dnx = sur[0] + surface[0];
-    dny = sur[1] + surface[1];
-    dnz = sur[2] + surface[2];
-    dnw = sur[3] + surface[3];
+    float x1 = s1[4*i1  ];
+    float y1 = s1[4*i1+1];
+    float z1 = s1[4*i1+2];
+    float w1 = s1[4*i1+3];
+    float x2 = s2[4*i2  ];
+    float y2 = s2[4*i2+1];
+    float z2 = s2[4*i2+2];
+    float w2 = s2[4*i2+3];
+
+    dnx = x1 + x2;
+    dny = y1 + y2;
+    dnz = z1 + z2;
+    dnw = w1 + w2;
 
     if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR ) return true;
 
-    dnx = sur[0] - surface[0];
-    dny = sur[1] - surface[1];
-    dnz = sur[2] - surface[2];
-    dnw = sur[3] - surface[3];
+    dnx = x1 - x2;
+    dny = y1 - y2;
+    dnz = z1 - z2;
+    dnw = w1 - w2;
 
     if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR ) return true;
 
     return false;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean isSame(TwistyObjectSurface s)
+    {
+    int n1 = surface.length/4;
+    int n2 = s.surface.length/4;
+
+    for(int n=0; n<n1; n++)
+      {
+      boolean exists = false;
+
+      for(int p=0; p<n2; p++)
+        if( surfaceSame(surface,n,s.surface,p) )
+          {
+          exists = true;
+          break;
+          }
+
+      if( !exists ) return false;
+      }
+
+    return true;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   TwistyObjectSurface rotateSurface(Static4D quat)
     {
-    float[] ret = new float[4];
-    QuatHelper.rotateVectorByQuat(ret,surface[0],surface[1],surface[2],0,quat);
-    ret[3] = surface[3];
+    int numPlanes = surface.length / 4;
+    float[] ret = new float[numPlanes*4];
+    float[] tmp = new float[4];
+
+    for(int p=0; p<numPlanes; p++)
+      {
+      QuatHelper.rotateVectorByQuat(tmp,surface[4*p],surface[4*p+1],surface[4*p+2],0,quat);
+      ret[4*p  ] = tmp[0];
+      ret[4*p+1] = tmp[1];
+      ret[4*p+2] = tmp[2];
+      ret[4*p+3] = surface[4*p+3];
+      }
+
     return new TwistyObjectSurface(ret);
     }
 }
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java b/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
index 57c61a22..04c4e732 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyDino4.java
@@ -29,6 +29,30 @@ public class TwistyDino4 extends TwistyDino
     super(iconMode, quat, move, scale, meta, asset);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// here we define 8 artificial monochromatic 'surfaces' - 8 vectors coming out from the center of the
+// cube to each vertex and some distance (doesn't matter)
+
+  private static final float[][] mMonoSurfaces =
+          {
+                  { 1, 1, 1, 1 },
+                  { 1, 1,-1, 1 },
+                  { 1,-1, 1, 1 },
+                  { 1,-1,-1, 1 },
+                  {-1, 1, 1, 1 },
+                  {-1, 1,-1, 1 },
+                  {-1,-1, 1, 1 },
+                  {-1,-1,-1, 1 },
+          };
+  private static final int[] mSurfaceIndices = { 4,2,2,4,1,2,7,4,1,1,7,7 };
+
+  @Override
+  public float[] getMonochromaticSurface(int variant, int cubitIndex, int faceIndex, float px, float py, float pz)
+    {
+    int index = mSurfaceIndices[cubitIndex];
+    return faceIndex<2 ? mMonoSurfaces[index] : null;
+    }
+/*
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
@@ -36,7 +60,7 @@ public class TwistyDino4 extends TwistyDino
     {
     return 1;
     }
-
+*/
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
