commit 49f67f9b094e35f3d9b6a25dae00f4f8da99721b
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 9 11:27:13 2020 +0000

    Progress with Pyraminx.

diff --git a/src/main/java/org/distorted/object/Cubit.java b/src/main/java/org/distorted/object/Cubit.java
index f6570ae4..12ddf677 100644
--- a/src/main/java/org/distorted/object/Cubit.java
+++ b/src/main/java/org/distorted/object/Cubit.java
@@ -124,7 +124,7 @@ class Cubit
 
   private int computeNearestAngle(float angle)
     {
-    final int NEAREST = 90;
+    final int NEAREST = 360/mParent.getBasicAngle();
 
     int tmp = (int)((angle+NEAREST/2)/NEAREST);
     if( angle< -(NEAREST*0.5) ) tmp-=1;
@@ -136,23 +136,20 @@ class Cubit
 
   private void modifyCurrentPosition(Static3D currentPosition, Static4D quat)
     {
-    float diff = 0.5f*(mParent.mSize-1);
-    float cubitCenterX = currentPosition.get0() - diff;
-    float cubitCenterY = currentPosition.get1() - diff;
-    float cubitCenterZ = currentPosition.get2() - diff;
+    float cubitCenterX = currentPosition.get0();
+    float cubitCenterY = currentPosition.get1();
+    float cubitCenterZ = currentPosition.get2();
 
     Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
     Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
 
-    float rotatedX = rotatedCenter.get0() + diff;
-    float rotatedY = rotatedCenter.get1() + diff;
-    float rotatedZ = rotatedCenter.get2() + diff;
+    float rotatedX = rotatedCenter.get0();
+    float rotatedY = rotatedCenter.get1();
+    float rotatedZ = rotatedCenter.get2();
 
-    int roundedX = (int)(rotatedX+0.1f);
-    int roundedY = (int)(rotatedY+0.1f);
-    int roundedZ = (int)(rotatedZ+0.1f);
+    currentPosition.set(rotatedX, rotatedY, rotatedZ);
+    mParent.clampPos(currentPosition);
 
-    currentPosition.set(roundedX, roundedY, roundedZ);
     computeRotationRow();
     }
 
@@ -184,8 +181,7 @@ class Cubit
     float y = position.get1();
     float z = position.get2();
 
-    float nc = parent.mSize*0.5f;
-    Static3D vector = new Static3D(x-nc+0.5f, y-nc+0.5f, z-nc+0.5f);
+    Static3D vector = new Static3D(x,y,z);
 
     mParent          = parent;
     mMesh            = mesh;
diff --git a/src/main/java/org/distorted/object/RubikCube.java b/src/main/java/org/distorted/object/RubikCube.java
index d3d6c414..0b21bf4b 100644
--- a/src/main/java/org/distorted/object/RubikCube.java
+++ b/src/main/java/org/distorted/object/RubikCube.java
@@ -89,6 +89,7 @@ class RubikCube extends RubikObject
     int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
     Static3D[] tmp = new Static3D[numCubits];
 
+    float diff = 0.5f*(size-1);
     int currentPosition = 0;
 
     for(int x = 0; x<size; x++)
@@ -96,7 +97,7 @@ class RubikCube extends RubikObject
         for(int z = 0; z<size; z++)
           if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
             {
-            tmp[currentPosition++] = new Static3D(x,y,z);
+            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
             }
 
     return tmp;
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index 092b4964..08e1c298 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -54,12 +54,13 @@ public abstract class RubikObject extends DistortedNode
   private final int NUM_CUBITS;
   private int mRotRow;
   private int mRotAxis;
+  private Static3D[] mOrigPos;
   private Static3D mScale, mNodeScale;
   private Static4D mQuatAccumulated;
   private Cubit[] mCubits;
+  private int mSize;
 
   float mStart, mStep;
-  int mSize;
 
   Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
   DistortedTexture mTexture;
@@ -77,15 +78,14 @@ public abstract class RubikObject extends DistortedNode
 
     resizeFBO(NODE_FBO_SIZE, NODE_FBO_SIZE);
 
-    Static3D[] positions = getCubitPositions(size);
+    mOrigPos = getCubitPositions(size);
 
     LEGAL_QUATS = getLegalQuats();
-    NUM_CUBITS  = positions.length;
+    NUM_CUBITS  = mOrigPos.length;
     ROTATION_AXIS = getRotationAxis();
 
     mSize = size;
-
-    computeStartAndStep(positions);
+    computeStartAndStep(mOrigPos);
 
     mRotationAngleStatic = new Static1D(0);
     mRotationAngleMiddle = new Static1D(0);
@@ -115,7 +115,7 @@ public abstract class RubikObject extends DistortedNode
     for(int i=0; i<NUM_CUBITS; i++)
       {
       MeshBase cubitMesh = createCubitMesh(vertices);
-      mCubits[i] = new Cubit(this,cubitMesh,positions[i]);
+      mCubits[i] = new Cubit(this,cubitMesh,mOrigPos[i]);
       textureCubitMesh(cubitMesh,i);
 
       attach(mCubits[i].mNode);
@@ -143,7 +143,7 @@ public abstract class RubikObject extends DistortedNode
       {
       for(int i=0; i<numFaces; i++)
         {
-        belongs = belongsToRotation(cubit, i, mSize-1 );
+        belongs = belongsToRotation(cubit, i, 0 );
         maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
         }
       }
@@ -213,6 +213,36 @@ public abstract class RubikObject extends DistortedNode
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
+
+  void clampPos(Static3D pos)
+    {
+    float currError, minError = Float.MAX_VALUE;
+    int minErrorIndex= -1;
+    float x = pos.get0();
+    float y = pos.get1();
+    float z = pos.get2();
+    float xo,yo,zo;
+
+    for(int i=0; i<NUM_CUBITS; i++)
+      {
+      xo = mOrigPos[i].get0();
+      yo = mOrigPos[i].get1();
+      zo = mOrigPos[i].get2();
+
+      currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
+
+      if( currError<minError )
+        {
+        minError = currError;
+        minErrorIndex = i;
+        }
+      }
+
+    pos.set( mOrigPos[minErrorIndex] );
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // the getFaceColors + final black in a horizontal strip.
 
diff --git a/src/main/java/org/distorted/object/RubikObjectList.java b/src/main/java/org/distorted/object/RubikObjectList.java
index 264d0df1..2267ca8c 100644
--- a/src/main/java/org/distorted/object/RubikObjectList.java
+++ b/src/main/java/org/distorted/object/RubikObjectList.java
@@ -29,10 +29,10 @@ import org.distorted.magic.R;
 
 public enum RubikObjectList
   {
-  CUBE2 ( 2, R.drawable.button2 , RubikCube.class, RubikCubeMovement.class),
-  CUBE3 ( 3, R.drawable.button3 , RubikCube.class, RubikCubeMovement.class),
-  CUBE4 ( 4, R.drawable.button4 , RubikCube.class, RubikCubeMovement.class),
-  CUBE5 ( 5, R.drawable.button5 , RubikCube.class, RubikCubeMovement.class),
+  CUBE2 ( 1, R.drawable.cube2, RubikCube.class, RubikCubeMovement.class),
+  CUBE3 ( 2, R.drawable.cube3, RubikCube.class, RubikCubeMovement.class),
+  CUBE4 ( 3, R.drawable.cube4, RubikCube.class, RubikCubeMovement.class),
+  CUBE5 ( 4, R.drawable.cube5, RubikCube.class, RubikCubeMovement.class),
   ;
 
   public static final int LENGTH = values().length;
@@ -86,7 +86,7 @@ public enum RubikObjectList
     DistortedEffects effects = new DistortedEffects();
     MeshRectangles mesh      = new MeshRectangles(20,20);   // mesh of the node, not of the cubits
 
-    return new RubikCube(mObjectSize, quatCur, quatAcc, texture, mesh, effects);
+    return new RubikPyraminx(mObjectSize, quatCur, quatAcc, texture, mesh, effects);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -94,6 +94,6 @@ public enum RubikObjectList
 
   public RubikObjectMovement getObjectMovementClass()
     {
-    return new RubikCubeMovement();
+    return new RubikPyraminxMovement();
     }
   }
diff --git a/src/main/java/org/distorted/object/RubikPyraminx.java b/src/main/java/org/distorted/object/RubikPyraminx.java
index 29f97a3e..da5fcc7e 100644
--- a/src/main/java/org/distorted/object/RubikPyraminx.java
+++ b/src/main/java/org/distorted/object/RubikPyraminx.java
@@ -46,9 +46,9 @@ public class RubikPyraminx extends RubikObject
   private static final Static3D[] AXIS = new Static3D[]
          {
            new Static3D(         0,        1,       0 ),
-           new Static3D( SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 ),
+           new Static3D(         0,  -1.0f/3, 2*SQ2/3 ),
            new Static3D(-SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 ),
-           new Static3D(         0,  -1.0f/3, 2*SQ2/3 )
+           new Static3D( SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 )
          };
 
   private static final int[] FACE_COLORS = new int[]
@@ -64,6 +64,8 @@ public class RubikPyraminx extends RubikObject
            SQ3/3, -SQ3/3, SQ3/6, -SQ3/6, SQ2*SQ3/3, -SQ2*SQ3/3, SQ2*SQ3/6, -SQ2*SQ3/6
          };
 
+  private Static4D[] mRotArray;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   RubikPyraminx(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
@@ -73,18 +75,76 @@ public class RubikPyraminx extends RubikObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+  private void emitRow(float x, float y, float z, float dx, float dy, float dz, int n, Static4D rot, Static3D[] array, int index)
+    {
+    for(int i=0; i<n; i++)
+      {
+      mRotArray[i+index] = rot;
+      array[i+index] = new Static3D(x+0.5f,y+SQ2*SQ3/12,z+SQ3/6);
+      x += dx;
+      y += dy;
+      z += dz;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int emitLowermost(float x, float y, float z, int n, Static3D[] array)
+    {
+    int added = 0;
+
+    for(int i=n; i>=1; i--)
+      {
+      emitRow(x,y,z, 1,0,0, i, null, array, added);
+      added += i;
+      x += 0.5f;
+      y += 0.0f;
+      z += SQ3/2;
+      }
+
+    return added;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int emitUpper(float x, float y, float z, int n, Static3D[] array, int index)
+    {
+    if( n>1 )
+      {
+      emitRow(x,y,z, 1,0,0, n-1, null, array, index);
+      index += (n-1);
+      emitRow(x+0.5f,y,z+SQ3/2, 0.5f,0,SQ3/2, n-1, null, array, index);
+      index += (n-1);
+      emitRow( x+n-1,y,z, -0.5f, 0, SQ3/2, n-1, null, array, index);
+      index += (n-1);
+      }
+    else
+      {
+      mRotArray[index] = null;
+      array[index] = new Static3D(x+0.5f,y+SQ2*SQ3/12,z+SQ3/6);
+      index++;
+      }
+
+    return index;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// size^2 + 3*(size-1) in the lowermost layer, then 6*(size-2) in the next, 6*(size-3) in the next,
+// ... 6 in the forelast, 1 in the last = 4size^2 - 6size +4 (if size>1)
+
   Static3D[] getCubitPositions(int size)
     {
-    int numCubits = (size*size*size + 6*size*size -4*size)/3;
+    int numCubits = size>1 ? 2*size*size-4*size+4:1;//4*size*size - 6*size +4 : 1;
     Static3D[] tmp = new Static3D[numCubits];
+    mRotArray = new Static4D[numCubits];
+
+    int currentIndex = emitLowermost( -0.5f*size, -(SQ2*SQ3/12)*size, -(SQ3/6)*size, size, tmp);
 
-    int currentPosition = 0;
-/*
-    ??
+    for(int i=size-1; i>=1; i--)
       {
-      tmp[currentPosition++] = new Static3D(x,y,z);
+      currentIndex = emitUpper( -0.5f*i, ((SQ2*SQ3)/12)*(3*size-4*i), -(SQ3/6)*i, i, tmp, currentIndex);
       }
-*/
+
     return tmp;
     }
 
@@ -116,14 +176,14 @@ public class RubikPyraminx extends RubikObject
     int xoffset = face*TEXTURE_HEIGHT;
     float STROKE = 0.05f*TEXTURE_HEIGHT;
     float OFF = STROKE/2 -1;
-    float OFF2 = 0.577f*TEXTURE_HEIGHT + OFF;
+    float OFF2 = 0.5f*TEXTURE_HEIGHT + OFF;
     float HEIGHT = TEXTURE_HEIGHT - OFF;
     float RADIUS = TEXTURE_HEIGHT/12;
-    float ARC1_H = 0.31f*TEXTURE_HEIGHT;
+    float ARC1_H = 0.2f*TEXTURE_HEIGHT;
     float ARC1_W = TEXTURE_HEIGHT*0.5f;
-    float ARC2_W = 0.152f*TEXTURE_HEIGHT;
-    float ARC2_H = 0.91f*TEXTURE_HEIGHT;
-    float ARC3_W = 0.847f*TEXTURE_HEIGHT;
+    float ARC2_W = 0.153f*TEXTURE_HEIGHT;
+    float ARC2_H = 0.905f*TEXTURE_HEIGHT;
+    float ARC3_W = TEXTURE_HEIGHT-ARC2_W;
 
     paint.setAntiAlias(true);
     paint.setStrokeWidth(STROKE);
diff --git a/src/main/res/drawable/button2.png b/src/main/res/drawable/button2.png
deleted file mode 100644
index eb781e64..00000000
Binary files a/src/main/res/drawable/button2.png and /dev/null differ
diff --git a/src/main/res/drawable/button3.png b/src/main/res/drawable/button3.png
deleted file mode 100644
index dae0f164..00000000
Binary files a/src/main/res/drawable/button3.png and /dev/null differ
diff --git a/src/main/res/drawable/button4.png b/src/main/res/drawable/button4.png
deleted file mode 100644
index c37a2e75..00000000
Binary files a/src/main/res/drawable/button4.png and /dev/null differ
diff --git a/src/main/res/drawable/button5.png b/src/main/res/drawable/button5.png
deleted file mode 100644
index c6c5ff3a..00000000
Binary files a/src/main/res/drawable/button5.png and /dev/null differ
diff --git a/src/main/res/drawable/cube2.png b/src/main/res/drawable/cube2.png
new file mode 100644
index 00000000..eb781e64
Binary files /dev/null and b/src/main/res/drawable/cube2.png differ
diff --git a/src/main/res/drawable/cube3.png b/src/main/res/drawable/cube3.png
new file mode 100644
index 00000000..dae0f164
Binary files /dev/null and b/src/main/res/drawable/cube3.png differ
diff --git a/src/main/res/drawable/cube4.png b/src/main/res/drawable/cube4.png
new file mode 100644
index 00000000..c37a2e75
Binary files /dev/null and b/src/main/res/drawable/cube4.png differ
diff --git a/src/main/res/drawable/cube5.png b/src/main/res/drawable/cube5.png
new file mode 100644
index 00000000..c6c5ff3a
Binary files /dev/null and b/src/main/res/drawable/cube5.png differ
