commit 4f629c09b884f3ba2592bdf09bb98c39e28b42d1
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon May 1 12:03:46 2023 +0200

    Coin Tetrahedron: initial commit

diff --git a/src/main/java/org/distorted/objectlib/main/ObjectSignatures.java b/src/main/java/org/distorted/objectlib/main/ObjectSignatures.java
index 7921bfe4..9199db4c 100644
--- a/src/main/java/org/distorted/objectlib/main/ObjectSignatures.java
+++ b/src/main/java/org/distorted/objectlib/main/ObjectSignatures.java
@@ -85,4 +85,5 @@ public class ObjectSignatures
   public static final int CA_333 = ObjectType.CA_333.ordinal();
   public static final int ICOS_2 = ObjectType.ICOS_2.ordinal();
   public static final int ICOS_3 = ObjectType.ICOS_3.ordinal();
+  public static final int COIN_3 = ObjectType.COIN_3.ordinal();
   }
diff --git a/src/main/java/org/distorted/objectlib/main/ObjectType.java b/src/main/java/org/distorted/objectlib/main/ObjectType.java
index 006db26a..6c16539b 100644
--- a/src/main/java/org/distorted/objectlib/main/ObjectType.java
+++ b/src/main/java/org/distorted/objectlib/main/ObjectType.java
@@ -100,6 +100,8 @@ 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
new file mode 100644
index 00000000..80a0c072
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyCoinTetrahedron.java
@@ -0,0 +1,378 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.objectlib.helpers.FactoryCubit;
+import org.distorted.objectlib.helpers.ObjectFaceShape;
+import org.distorted.objectlib.helpers.ObjectShape;
+import org.distorted.objectlib.helpers.ObjectSignature;
+import org.distorted.objectlib.helpers.ObjectVertexEffects;
+import org.distorted.objectlib.main.InitAssets;
+import org.distorted.objectlib.main.InitData;
+import org.distorted.objectlib.main.ObjectSignatures;
+import org.distorted.objectlib.main.ObjectType;
+import org.distorted.objectlib.scrambling.ScrambleEdgeGenerator;
+import org.distorted.objectlib.shape.ShapeHexahedron;
+import org.distorted.objectlib.touchcontrol.TouchControlTetrahedron;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TwistyCoinTetrahedron extends ShapeHexahedron
+{
+  static final Static3D[] ROT_AXIS = new Static3D[]
+         {
+           new Static3D(     0,-SQ3/3,-SQ6/3),
+           new Static3D(     0,-SQ3/3, SQ6/3),
+           new Static3D( SQ6/3, SQ3/3,     0),
+           new Static3D(-SQ6/3, SQ3/3,     0),
+         };
+
+  private int[][] mEdges;
+  private int[][] mBasicAngle;
+  private float[][] mCuts;
+  private float[][] mPosition;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public TwistyCoinTetrahedron(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InitData data, InitAssets asset)
+    {
+    super(meshState, iconMode, data.getNumLayers()[0], quat, move, scale, data, asset);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] getScrambleEdges()
+    {
+    if( mEdges==null ) mEdges = ScrambleEdgeGenerator.getScrambleEdgesSingle(mBasicAngle);
+    return mEdges;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[][] getCuts(int[] numLayers)
+    {
+    if( mCuts==null )
+      {
+      float[] cut = new float[] {-0.95f*(SQ6/4),0};
+      mCuts = new float[][] { cut,cut,cut,cut };
+      }
+
+    return mCuts;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean[][] getLayerRotatable(int[] numLayers)
+    {
+    boolean[] tmp = new boolean[] {true,false,true};
+    return new boolean[][] { tmp,tmp,tmp,tmp };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getTouchControlType()
+    {
+    return TC_TETRAHEDRON;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getTouchControlSplit()
+    {
+    return TYPE_SPLIT_EDGE_COIN;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][][] getEnabled()
+    {
+    return new int[][][]
+      {
+         {{0},{0},{0},{1},{3},{2}},
+         {{1},{1},{1},{0},{2},{3}},
+         {{2},{2},{2},{3},{1},{0}},
+         {{3},{3},{3},{2},{0},{1}}
+      };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[] getDist3D(int[] numLayers)
+    {
+    return TouchControlTetrahedron.D3D;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Static3D[] getFaceAxis()
+    {
+    return TouchControlTetrahedron.FACE_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[][] getCubitPositions(int[] numLayers)
+    {
+    if( mPosition==null )
+      {
+      mPosition = new float[][]
+         {
+            // TODO
+         };
+      }
+
+    return mPosition;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Static4D getCubitQuats(int cubit, int[] numLayers)
+    {
+    switch(cubit)
+      {
+      // TODO
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] getVertices(int variant)
+    {
+    if( variant==0 )
+      {
+      return new float[][]
+          {
+             // TODO
+          };
+      }
+    else if( variant==1 )
+      {
+      return new float[][]
+          {
+             // TODO
+          };
+      }
+    else
+      {
+      return new float[][]
+          {
+             // TODO
+          };
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectShape getObjectShape(int variant)
+    {
+    if( variant==0 )
+      {
+      int[][] indices =
+          {
+             // TODO
+          };
+
+      return new ObjectShape(getVertices(variant), indices);
+      }
+    else if( variant==1 )
+      {
+      int[][] indices =
+          {
+             // TODO
+          };
+
+      return new ObjectShape(getVertices(variant), indices);
+      }
+    else
+      {
+      int[][] indices =
+          {
+             // TODO
+          };
+
+      return new ObjectShape(getVertices(variant), indices);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  public ObjectFaceShape getObjectFaceShape(int variant)
+    {
+    if( variant==0 )
+      {
+      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 };
+      return new ObjectFaceShape(bands,indices,null);
+      }
+    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 };
+      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 };
+      return new ObjectFaceShape(bands,indices,null);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectVertexEffects getVertexEffects(int variant)
+    {
+    if( variant==0 )
+      {
+      float[][] corners = { {0.06f,0.12f} };
+      int[] indices     = {  };  // TODO
+      float[][] centers = { { 0.0f, 0.0f, 0.0f} };
+      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
+      return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
+      }
+    else
+      {
+      float[][] corners = { {0.06f,0.20f} };
+      int[] indices     = {  }; // TODO
+      float[][] centers = { { } };  // TODO
+      return FactoryCubit.generateVertexEffect(getVertices(variant),corners,indices,centers,indices);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getNumCubitVariants(int[] numLayers)
+    {
+    return 3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getCubitVariant(int cubit, int[] numLayers)
+    {
+    return cubit<4 ? 0 : (cubit<16 ? 1:2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getStickerRadius()
+    {
+    return 0.09f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getStickerStroke()
+    {
+    return isInIconMode() ? 0.20f : 0.09f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[][] getStickerAngles()
+    {
+    return null;   // TODO
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] getBasicAngles()
+    {
+    if( mBasicAngle ==null )
+      {
+      int[] tmp = {3,3,3};
+      mBasicAngle = new int[][] { tmp,tmp,tmp,tmp };
+      }
+
+    return mBasicAngle;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getShortName()
+    {
+    return ObjectType.COIN_3.name();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectSignature getSignature()
+    {
+    return new ObjectSignature(ObjectSignatures.COIN_3);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getObjectName()
+    {
+    return "Coin Tetrahedron";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String getInventor()
+    {
+    return "Kevin Uhrik";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getYearOfInvention()
+    {
+    return 2019;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getComplexity()
+    {
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String[][] getTutorials()
+    {
+    return new String[][] {
+                          {"gb","2WrDNfcfpAg","Coin Pyraminx Tutorial","Brent Richter"},
+                          {"es","5pTfSCsHPWs","Tutorial Coin Pyraminx","Kubekings"},
+                          {"ru","qUMQsn8kg1A","Как собрать Коин Пираминкс","Алексей Ярыгин"},
+                          {"vn","TOaiOcEE0lk","Hướng Dẫn Giải Pyraminx Coin","Rubik Cube"},
+                         };
+    }
+}
