commit b480f4dd13c9ec87415a0a0e9f9f7368adce1acc
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri May 5 23:29:06 2023 +0200

    Coin Tetrahedron: progress

diff --git a/src/main/java/org/distorted/objectlib/main/ObjectType.java b/src/main/java/org/distorted/objectlib/main/ObjectType.java
index 6c16539b..70608d2a 100644
--- a/src/main/java/org/distorted/objectlib/main/ObjectType.java
+++ b/src/main/java/org/distorted/objectlib/main/ObjectType.java
@@ -22,6 +22,8 @@ import org.distorted.objectlib.objects.*;
 
 public enum ObjectType
   {
+  COIN_3 ( TwistyCoinTetrahedron.class, 15, R.drawable.ball_4, true,    40, new InitData(new int[] {3,3,3,3})),
+
   CUBE_2 ( TwistyCuboid.class         , 12, R.drawable.cube_2, true,     0, new InitData(new int[] {2,2,2})),
   CUBE_3 ( TwistyCuboid.class         , 17, R.drawable.cube_3, true,     0, new InitData(new int[] {3,3,3})),
   CUBE_4 ( TwistyCuboid.class         , 24, R.drawable.cube_4, true,    50, new InitData(new int[] {4,4,4})),
@@ -100,8 +102,6 @@ public enum ObjectType
   PDUO_2 ( TwistyPyraminxDuo.class    ,  4, R.drawable.pduo_2, true,     0, new InitData(new int[] {2,2,2,2})),
   PDIA_3 ( TwistyPyraminxDiamond.class, 12, R.drawable.pdia_3, true,    40, new InitData(new int[] {3,3,3})),
 
-  COIN_3 ( TwistyCoinTetrahedron.class, 15, R.drawable.ball_4, true,    40, new InitData(new int[] {3,3,3,3})),
-
   BALL_4 ( TwistyMasterball.class     , 28, R.drawable.ball_4, true,    70, new InitData(new int[] {4,2,2,2,2})),
   BAN5_4 ( TwistyBandagedCuboid.class , 48, R.drawable.ban5_4, false,   50, new InitData(new int[] {4,4,4}, TwistyBandagedCuboid.POS_5)),
   BAN6_4 ( TwistyBandagedCuboid.class ,487, R.drawable.ban6_4, false,   50, new InitData(new int[] {4,4,4}, TwistyBandagedCuboid.POS_6)),
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyCoinTetrahedron.java b/src/main/java/org/distorted/objectlib/objects/TwistyCoinTetrahedron.java
index 80a0c072..cfd98f0d 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyCoinTetrahedron.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyCoinTetrahedron.java
@@ -12,6 +12,7 @@ package org.distorted.objectlib.objects;
 import static org.distorted.objectlib.touchcontrol.TouchControl.TC_TETRAHEDRON;
 import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_SPLIT_EDGE_COIN;
 
+import org.distorted.library.main.QuatHelper;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objectlib.helpers.FactoryCubit;
@@ -39,10 +40,16 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
            new Static3D(-SQ6/3, SQ3/3,     0),
          };
 
+  private static final int N = 5;
+  private static final float D = 0.9f;
+
   private int[][] mEdges;
   private int[][] mBasicAngle;
   private float[][] mCuts;
   private float[][] mPosition;
+  private int[] mQuatIndex;
+
+  private float[][] V,M,L;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -121,15 +128,76 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
     return TouchControlTetrahedron.FACE_AXIS;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void initVertices()
+    {
+    V = new float[][]
+        {
+            { 0.0f,-0.75f*SQ2,-1.5f},
+            { 0.0f,-0.75f*SQ2, 1.5f},
+            { 1.5f, 0.75f*SQ2, 0.0f},
+            {-1.5f, 0.75f*SQ2, 0.0f},
+        };
+
+    float A = 1.04f;
+    M = new float[4][3];
+
+    for(int i=0; i<4; i++)
+      for(int j=0; j<3; j++)
+        M[i][j] = A*(V[0][j]+V[1][j]+V[2][j]+V[3][j]-V[i][j])/3;
+
+    L = new float[12][3];
+
+    for(int i=0; i<3; i++)
+      {
+      L[ 0][i] = 0.5f*V[1][i] + 0.25f*V[2][i] + 0.25f*V[3][i];
+      L[ 1][i] = 0.5f*V[3][i] + 0.25f*V[1][i] + 0.25f*V[2][i];
+      L[ 2][i] = 0.5f*V[2][i] + 0.25f*V[1][i] + 0.25f*V[3][i];
+      L[ 3][i] = 0.5f*V[0][i] + 0.25f*V[2][i] + 0.25f*V[3][i];
+      L[ 4][i] = 0.5f*V[2][i] + 0.25f*V[0][i] + 0.25f*V[3][i];
+      L[ 5][i] = 0.5f*V[3][i] + 0.25f*V[0][i] + 0.25f*V[2][i];
+      L[ 6][i] = 0.5f*V[3][i] + 0.25f*V[1][i] + 0.25f*V[0][i];
+      L[ 7][i] = 0.5f*V[0][i] + 0.25f*V[1][i] + 0.25f*V[3][i];
+      L[ 8][i] = 0.5f*V[1][i] + 0.25f*V[0][i] + 0.25f*V[3][i];
+      L[ 9][i] = 0.5f*V[2][i] + 0.25f*V[1][i] + 0.25f*V[0][i];
+      L[10][i] = 0.5f*V[0][i] + 0.25f*V[1][i] + 0.25f*V[2][i];
+      L[11][i] = 0.5f*V[1][i] + 0.25f*V[0][i] + 0.25f*V[2][i];
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public float[][] getCubitPositions(int[] numLayers)
     {
     if( mPosition==null )
       {
+      if( V==null ) initVertices();
+
       mPosition = new float[][]
          {
-            // TODO
+             {V[1][0],V[1][1],V[1][2]},
+             {V[0][0],V[0][1],V[0][2]},
+             {V[2][0],V[2][1],V[2][2]},
+             {V[3][0],V[3][1],V[3][2]},
+
+             {M[0][0],M[0][1],M[0][2]},
+             {M[1][0],M[1][1],M[1][2]},
+             {M[2][0],M[2][1],M[2][2]},
+             {M[3][0],M[3][1],M[3][2]},
+
+             {L[ 0][0],L[ 0][1],L[ 0][2]},
+             {L[ 1][0],L[ 1][1],L[ 1][2]},
+             {L[ 2][0],L[ 2][1],L[ 2][2]},
+             {L[ 3][0],L[ 3][1],L[ 3][2]},
+             {L[ 4][0],L[ 4][1],L[ 4][2]},
+             {L[ 5][0],L[ 5][1],L[ 5][2]},
+             {L[ 6][0],L[ 6][1],L[ 6][2]},
+             {L[ 7][0],L[ 7][1],L[ 7][2]},
+             {L[ 8][0],L[ 8][1],L[ 8][2]},
+             {L[ 9][0],L[ 9][1],L[ 9][2]},
+             {L[10][0],L[10][1],L[10][2]},
+             {L[11][0],L[11][1],L[11][2]},
          };
       }
 
@@ -140,39 +208,257 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
 
   public Static4D getCubitQuats(int cubit, int[] numLayers)
     {
-    switch(cubit)
+    if( mQuatIndex==null ) mQuatIndex = new int[] {0,6,1,2,
+                                                   0,5,4,3,
+                                                   0,2,1,10,8,5,11,7,4,9,6,3 };
+    return mObjectQuats[mQuatIndex[cubit]];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] rotateVertices(float angle, float[] vector, float[] center, float[] rotAxis)
+    {
+    float[] ret = new float[4];
+    float sin = (float)Math.sin(angle/2);
+    float cos = (float)Math.cos(angle/2);
+    float[] quat= new float[] { sin*rotAxis[0], sin*rotAxis[1], sin*rotAxis[2], cos};
+    QuatHelper.rotateVectorByQuat(ret,vector,quat);
+
+    ret[0] += center[0];
+    ret[1] += center[1];
+    ret[2] += center[2];
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void normalize(float[] v)
+    {
+    float len = (float)Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+    v[0] /= len;
+    v[1] /= len;
+    v[2] /= len;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] produceCorner()
+    {
+    float[][] ret = new float[5 + 3*(N+1)][];
+
+    ret[0] = new float[] { 0.00f, 0.0f     , 0.00f };
+    ret[1] = new float[] { 0.00f, 0.0f     ,-1.50f };
+    ret[2] = new float[] { 0.75f, 0.75f*SQ2,-0.75f };
+    ret[3] = new float[] {-0.75f, 0.75f*SQ2,-0.75f };
+    ret[4] = new float[] { 0.00f, 0.75f*SQ2,-1.50f };
+
+    float[] vect1 = new float[4];
+    float[] vect2 = new float[4];
+    float[] vect3 = new float[4];
+
+    for(int i=0; i<3; i++)
+      {
+      vect1[i] = -D*M[0][i] + D*(V[1][i]+V[3][i])/2;
+      vect2[i] = -D*M[3][i] + D*(V[1][i]+V[2][i])/2;
+      vect3[i] = -D*M[2][i] + D*(V[0][i]+V[1][i])/2;
+      }
+
+    float[] rot1 = new float[] { V[0][0]-M[0][0], V[0][1]-M[0][1], V[0][2]-M[0][2] };
+    float[] rot2 = new float[] { V[3][0]-M[3][0], V[3][1]-M[3][1], V[3][2]-M[3][2] };
+    float[] rot3 = new float[] { V[2][0]-M[2][0], V[2][1]-M[2][1], V[2][2]-M[2][2] };
+
+    normalize(rot1);
+    normalize(rot2);
+    normalize(rot3);
+
+    float[] center1 = { M[0][0]-V[1][0], M[0][1]-V[1][1], M[0][2]-V[1][2] };
+    float[] center2 = { M[3][0]-V[1][0], M[3][1]-V[1][1], M[3][2]-V[1][2] };
+    float[] center3 = { M[2][0]-V[1][0], M[2][1]-V[1][1], M[2][2]-V[1][2] };
+
+    for(int i=0; i<N+1; i++)
+      {
+      float angle = (float)(2*Math.PI/3)*i/N;
+
+      ret[5        +i] = rotateVertices(angle,vect1,center1,rot1);
+      ret[5+   N+1 +i] = rotateVertices(angle,vect2,center2,rot2);
+      ret[5+2*(N+1)+i] = rotateVertices(angle,vect3,center3,rot3);
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] produceCenter()
+    {
+    float[][] ret = new float[3*N+1][];
+
+    float[] rot = new float[] { V[0][0]-M[0][0], V[0][1]-M[0][1], V[0][2]-M[0][2] };
+    normalize(rot);
+
+    float[] center1 = {   0.0f,-D*SQ2  , D  };
+    float[] center2 = { D*1.5f, D*SQ2/2,-D/2};
+    float[] center3 = {-D*1.5f, D*SQ2/2,-D/2};
+
+    float[] vect1 = {  D*0.75f, D*0.75f*SQ2, -D*0.75f, 0.0f };
+    float[] vect2 = { -D*1.50f,   0.00f    ,    0.00f, 0.0f };
+    float[] vect3 = {  D*0.75f,-D*0.75f*SQ2,  D*0.75f, 0.0f };
+
+    for(int i=0; i<N; i++)
+      {
+      float angle = (float)(Math.PI/3)*i/N;
+
+      ret[  N-1-i] = rotateVertices(angle, vect1, center1, rot);
+      ret[2*N-1-i] = rotateVertices(angle, vect2, center2, rot);
+      ret[3*N-1-i] = rotateVertices(angle, vect3, center3, rot);
+      }
+
+    ret[3*N] = new float[] { 0.0f, -SQ2/4, -0.5f };
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] produceLeaf()
+    {
+    float[][] ret = new float[2*N+1][];
+
+    float[] rot = new float[] { V[0][0]-M[0][0], V[0][1]-M[0][1], V[0][2]-M[0][2] };
+    normalize(rot);
+
+    float[] center1 = { 0.0f, -D*0.75f*SQ2, D*0.75f };
+    float[] center2 = { 0.0f,    0.25f*SQ2,  -0.25f };
+
+    float[] vect1 = {  D*0.75f, D*0.75f*SQ2, -D*0.75f, 0.0f };
+    float[] vect2 = new float[4];
+    for(int i=0; i<3; i++) vect2[i] = -D*M[0][i] + D*(V[1][i]+V[3][i])/2;
+
+    for(int i=0; i<N; i++)
       {
-      // TODO
+      float angle1 = (float)(  Math.PI/3)*i/N;
+      float angle2 = (float)(2*Math.PI/3)*i/N;
+
+      ret[  N-1-i] = rotateVertices(angle1, vect1, center1, rot);
+      ret[2*N-1-i] = rotateVertices(angle2, vect2, center2, rot);
       }
 
-    return null;
+    ret[2*N] = new float[] { 0.0f, -0.75f*SQ2/10, -1.5f/10 };
+
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private float[][] getVertices(int variant)
     {
-    if( variant==0 )
+         if( variant==0 ) return produceCorner();
+    else if( variant==1 ) return produceCenter();
+    else                  return produceLeaf();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[][] produceCornerShape()
+    {
+    int[][] ret = new int[9+3*N][];
+
+    ret[0] = new int[N+4];
+    ret[0][0] = 3;
+    ret[0][1] = 0;
+    ret[0][2] = 2;
+    for(int i=0; i<N+1; i++) ret[0][3+i] = 5+N-i;
+
+    ret[1] = new int[N+4];
+    ret[1][0] = 2;
+    ret[1][1] = 0;
+    ret[1][2] = 1;
+    for(int i=0; i<N+1; i++) ret[1][3+i] = 6+2*N-i;
+
+    ret[2] = new int[N+4];
+    ret[2][0] = 1;
+    ret[2][1] = 0;
+    ret[2][2] = 3;
+    for(int i=0; i<N+1; i++) ret[2][3+i] = 7+3*N-i;
+
+    ret[3] = new int[] {5+  N,2    , 4 };
+    ret[4] = new int[] {2    ,6+  N, 4 };
+    ret[5] = new int[] {6+2*N,1    , 4 };
+    ret[6] = new int[] {1    ,7+2*N, 4 };
+    ret[7] = new int[] {7+3*N,3    , 4 };
+    ret[8] = new int[] {3    ,5    , 4 };
+
+    for(int i=0; i<N; i++)
       {
-      return new float[][]
-          {
-             // TODO
-          };
+      ret[9+    i] = new int[] {5    +i, 6    +i, 4};
+      ret[9+  N+i] = new int[] {6+  N+i, 7+  N+i, 4};
+      ret[9+2*N+i] = new int[] {7+2*N+i, 8+2*N+i, 4};
       }
-    else if( variant==1 )
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[][] produceCenterShape()
+    {
+    int[][] ret = new int[3+3*(N-1)+1][];
+
+    ret[0] = new int[3*N];
+    for(int i=0; i<3*N; i++) ret[0][i] = 3*N-1-i;
+
+    for(int i=1; i<=N-1; i++)
       {
-      return new float[][]
-          {
-             // TODO
-          };
+      ret[i] = new int[3];
+      ret[i][0] = i;
+      ret[i][1] = i-1;
+      ret[i][2] = 3*N;
+
+      ret[N-1+i] = new int[3];
+      ret[N-1+i][0] = N+i;
+      ret[N-1+i][1] = N+i-1;
+      ret[N-1+i][2] = 3*N;
+
+      ret[2*N-2+i] = new int[3];
+      ret[2*N-2+i][0] = 2*N+i;
+      ret[2*N-2+i][1] = 2*N+i-1;
+      ret[2*N-2+i][2] = 3*N;
       }
-    else
+
+    ret[3*N-2] = new int[] {   N-1,   N, 3*N };
+    ret[3*N-1] = new int[] { 2*N-1, 2*N, 3*N };
+    ret[3*N  ] = new int[] { 3*N-1,   0, 3*N };
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[][] produceLeafShape()
+    {
+    int[][] ret = new int[2+2*(N-1)+1][];
+
+    ret[0] = new int[2*N];
+    for(int i=0; i<2*N; i++) ret[0][i] = 2*N-1-i;
+
+    for(int i=1; i<=N-1; i++)
       {
-      return new float[][]
-          {
-             // TODO
-          };
+      ret[i] = new int[3];
+      ret[i][0] = i;
+      ret[i][1] = i-1;
+      ret[i][2] = 2*N;
+
+      ret[N-1+i] = new int[3];
+      ret[N-1+i][0] = N+i;
+      ret[N-1+i][1] = N+i-1;
+      ret[N-1+i][2] = 2*N;
       }
+
+    ret[2*N-1] = new int[] { N,  N-1, 2*N };
+    ret[2*N  ] = new int[] { 0,2*N-1, 2*N };
+
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -181,60 +467,54 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
     {
     if( variant==0 )
       {
-      int[][] indices =
-          {
-             // TODO
-          };
-
+      int[][] indices = produceCornerShape();
       return new ObjectShape(getVertices(variant), indices);
       }
     else if( variant==1 )
       {
-      int[][] indices =
-          {
-             // TODO
-          };
-
+      int[][] indices = produceCenterShape();
       return new ObjectShape(getVertices(variant), indices);
       }
     else
       {
-      int[][] indices =
-          {
-             // TODO
-          };
-
+      int[][] indices = produceLeafShape();
       return new ObjectShape(getVertices(variant), indices);
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO
 
   public ObjectFaceShape getObjectFaceShape(int variant)
     {
     if( variant==0 )
       {
-      float h1 = isInIconMode() ? 0.001f : 0.06f;
+      float h1 = isInIconMode() ? 0.001f : 0.035f;
       float h2 = isInIconMode() ? 0.001f : 0.01f;
-      float[][] bands = { {h1,35,0.5f,0.7f,5,2,2}, {h2,35,0.2f,0.4f,5,2,2} };
-      int[] indices   = { 0,0,0,1,1,1,1,1,1 };
-      return new ObjectFaceShape(bands,indices,null);
+      float[][] bands = { {h1,35,0.2f,0.4f,5,1,0}, {h2,35,0.2f,0.4f,2,0,0} };
+      int[] indices   = new int[9+3*N];
+      for(int i=3; i<9+3*N; i++) indices[i] = 1;
+      float A = 0.5f;
+      float[] convexCenter = { 0.0f*A, 0.75f*SQ2*A, -1.50f*A};
+      return new ObjectFaceShape(bands,indices,convexCenter);
       }
     else if( variant==1 )
       {
-      float h1 = isInIconMode() ? 0.001f : 0.06f;
-      float h2 = isInIconMode() ? 0.001f : 0.01f;
-      float[][] bands = { {h1,35,0.5f,0.7f,5,2,2}, {h2,35,0.2f,0.4f,5,2,2} };
-      int[] indices   = { 0,0,0,1,1,1,1,1,1 };
+      float h1 = isInIconMode() ? 0.001f : 0.00f;
+      float h2 = isInIconMode() ? 0.001f : 0.00f;
+      float[][] bands = { {h1,15,0.1f,0.2f,5,0,0}, {h2,15,0.1f,0.2f,2,0,0} };
+      int num = 3+3*(N-1)+1;
+      int[] indices   = new int[num];
+      for(int i=1; i<num; i++) indices[i] = 1;
       return new ObjectFaceShape(bands,indices,null);
       }
     else
       {
-      float h1 = isInIconMode() ? 0.001f : 0.038f;
-      float h2 = isInIconMode() ? 0.001f : 0.020f;
-      float[][] bands = { {h1,35,0.250f,0.7f,7,2,2}, {h2,35,0.125f,0.2f,3,1,2}, {h2,35,0.125f,0.2f,3,1,1} };
-      int[] indices   = { 0,0,1,1,2,2 };
+      float h1 = isInIconMode() ? 0.001f : 0.0f;
+      float h2 = isInIconMode() ? 0.001f : 0.0f;
+      float[][] bands = { {h1,15,0.250f,0.7f,5,0,0}, {h2,15,0.125f,0.2f,2,0,0} };
+      int num = 2+2*(N-1)+1;
+      int[] indices   = new int[num];
+      for(int i=1; i<num; i++) indices[i] = 1;
       return new ObjectFaceShape(bands,indices,null);
       }
     }
@@ -245,23 +525,26 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
     {
     if( variant==0 )
       {
-      float[][] corners = { {0.06f,0.12f} };
-      int[] indices     = {  };  // TODO
-      float[][] centers = { { 0.0f, 0.0f, 0.0f} };
+      float[][] corners = { {0.04f,0.15f} };
+      int[] indices     = new int[5 + 3*(N+1)];
+      for(int i=4; i<5 + 3*(N+1); i++) indices[i] = -1;
+      float[][] centers = { { 0.00f, 0.75f*SQ2,-1.50f} };
       return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
       }
     else if( variant==1 )
       {
       float[][] corners = { {0.06f,0.12f} };
-      int[] indices     = {  };  // TODO
-      float[][] centers = { { } };  // TODO
+      int[] indices     = new int[3*N+1];
+      for(int i=0; i<3*N+1; i++) indices[i] = -1;
+      float[][] centers = { { 0,0,0 } };
       return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
       }
     else
       {
       float[][] corners = { {0.06f,0.20f} };
-      int[] indices     = {  }; // TODO
-      float[][] centers = { { } };  // TODO
+      int[] indices     = new int[2*N+1];
+      for(int i=0; i<2*N+1; i++) indices[i] = -1;
+      float[][] centers = { { 0,0,0 } };
       return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
       }
     }
@@ -277,7 +560,7 @@ public class TwistyCoinTetrahedron extends ShapeHexahedron
 
   public int getCubitVariant(int cubit, int[] numLayers)
     {
-    return cubit<4 ? 0 : (cubit<16 ? 1:2);
+    return cubit<4 ? 0 : (cubit<8 ? 1:2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
