commit bbc6da6c5efeacb704261cd85ff10212f9d161a9
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Dec 21 18:55:57 2020 +0100

    Megaminx family: part1 (doesn't work yet)

diff --git a/src/main/java/org/distorted/objects/FactoryCubit.java b/src/main/java/org/distorted/objects/FactoryCubit.java
index 76a9260e..7b95ffa8 100644
--- a/src/main/java/org/distorted/objects/FactoryCubit.java
+++ b/src/main/java/org/distorted/objects/FactoryCubit.java
@@ -674,6 +674,34 @@ class FactoryCubit
     return new MeshJoined(meshes);
     }
 
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesMinxCorner()
+    {
+    MeshBase[] meshes = new MeshPolygon[6];
+
+    float[] vertices0 = {  };   // TODO
+    float[] bands0 = computeBands(0.03f,27,0.25f,0.5f,5);
+    float[] vertices1 = {  };   // TODO
+    float[] bands1 = computeBands(0.00f,27,0.25f,0.5f,2);
+
+    meshes[0] = new MeshPolygon(vertices0, bands0, 1, 1);
+    meshes[0].setEffectAssociation(0, 1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0, 2,0);
+    meshes[2] = meshes[0].copy(true);
+    meshes[2].setEffectAssociation(0, 4,0);
+    meshes[3] = new MeshPolygon(vertices1, bands1, 0, 0);
+    meshes[3].setEffectAssociation(0, 8,0);
+    meshes[4] = meshes[3].copy(true);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[3].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // EFFECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1171,6 +1199,15 @@ class FactoryCubit
     return effect;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsMinxCorner()
+    {
+    // TODO
+
+    return null;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // OBJECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1521,6 +1558,25 @@ class FactoryCubit
 
     mesh.mergeEffComponents();
 
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createMinxCornerMesh()
+    {
+    MeshBase mesh = createFacesMinxCorner();
+    VertexEffect[] effects = createVertexEffectsMinxCorner();
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+/*
+    Static3D center = new Static3D(0.0f,-0.5f,-0.5f);
+    Static3D[] vertices = new Static3D[2];
+    vertices[0] = new Static3D(+0.5f,+0.0f,+0.0f);
+    vertices[1] = new Static3D(-0.5f,+0.0f,+0.0f);
+    roundCorners(mesh,center,vertices,0.06f,0.10f);
+*/
+    mesh.mergeEffComponents();
+
     return mesh;
     }
   }
diff --git a/src/main/java/org/distorted/objects/MovementMinx.java b/src/main/java/org/distorted/objects/MovementMinx.java
new file mode 100644
index 00000000..2381efe6
--- /dev/null
+++ b/src/main/java/org/distorted/objects/MovementMinx.java
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objects;
+
+import org.distorted.library.type.Static3D;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class MovementMinx extends Movement
+{
+  static final float DIST3D = 0.25f;
+  static final float DIST2D = 0.25f;
+
+  static final Static3D[] FACE_AXIS = new Static3D[]
+         {
+           new Static3D(1,0,0), new Static3D(-1,0,0),
+           new Static3D(0,1,0), new Static3D(0,-1,0),
+           new Static3D(0,0,1), new Static3D(0,0,-1)
+         };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MovementMinx()
+    {
+    super(TwistyMinx.ROT_AXIS, FACE_AXIS, DIST3D, DIST2D);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int computeRowFromOffset(int face, int size, float offset)
+    {
+    return offset<DIST2D ? 0:1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float returnRotationFactor(int size, int row)
+    {
+    return 1.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// faces 0,1,2,3  --> /
+// faces 4,5      --> \
+
+  private boolean isTopHalf(int face, float[] touchPoint)
+    {
+    if( face==4 || face==5 ) return touchPoint[1] >=-touchPoint[0];
+    else                     return touchPoint[1] >= touchPoint[0];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean isInsideFace(int face, float[] p)
+    {
+    return ( p[0]<=DIST2D && p[0]>=-DIST2D && p[1]<=DIST2D && p[1]>=-DIST2D );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 0 +++
+// 1 ++-
+// 2 +-+
+// 3 +--
+
+  void computeEnabledAxis(int face, float[] touchPoint, int[] enabled)
+    {
+    enabled[0] = 1;
+
+    boolean isTop = isTopHalf(face,touchPoint);
+
+    switch(face)
+      {
+      case 0: enabled[1] = isTop ? 0:3; break;
+      case 1: enabled[1] = isTop ? 2:1; break;
+      case 2: enabled[1] = isTop ? 2:0; break;
+      case 3: enabled[1] = isTop ? 1:3; break;
+      case 4: enabled[1] = isTop ? 0:1; break;
+      case 5: enabled[1] = isTop ? 2:3; break;
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objects/ObjectList.java b/src/main/java/org/distorted/objects/ObjectList.java
index d8a7baea..74e9222b 100644
--- a/src/main/java/org/distorted/objects/ObjectList.java
+++ b/src/main/java/org/distorted/objects/ObjectList.java
@@ -112,7 +112,7 @@ public enum ObjectList
          3
        ),
 
-   IVY (
+  IVY  (
          new int[][] {
                        {2 , 8, R.raw.ivy, R.drawable.ui_small_ivy, R.drawable.ui_medium_ivy, R.drawable.ui_big_ivy, R.drawable.ui_huge_ivy} ,
                      },
@@ -121,7 +121,7 @@ public enum ObjectList
          3
        ),
 
-   REX (
+  REX  (
          new int[][] {
                        {3 , 16, R.raw.rex, R.drawable.ui_small_rex, R.drawable.ui_medium_rex, R.drawable.ui_big_rex, R.drawable.ui_huge_rex} ,
                      },
@@ -129,6 +129,15 @@ public enum ObjectList
          new MovementRex(),
          3
        ),
+
+  MINX (
+         new int[][] {
+                       {3 , 18, R.raw.rex, R.drawable.ui_small_rex, R.drawable.ui_medium_rex, R.drawable.ui_big_rex, R.drawable.ui_huge_rex} ,
+                     },
+         TwistyMinx.class,
+         new MovementMinx(),
+         4
+       ),
   ;
 
   public static final int NUM_OBJECTS = values().length;
@@ -501,16 +510,17 @@ public enum ObjectList
 
     switch(ordinal())
       {
-      case 0: return new TwistyCube      (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 1: return new TwistyPyraminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 2: return new TwistyDiamond   (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 3: return new TwistyDino6     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 4: return new TwistyDino4     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 5: return new TwistyRedi      (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 6: return new TwistyHelicopter(size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 7: return new TwistySkewb     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 8: return new TwistyIvy       (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 9: return new TwistyRex       (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  0: return new TwistyCube      (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  1: return new TwistyPyraminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  2: return new TwistyDiamond   (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  3: return new TwistyDino6     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  4: return new TwistyDino4     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  5: return new TwistyRedi      (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  6: return new TwistyHelicopter(size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  7: return new TwistySkewb     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  8: return new TwistyIvy       (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  9: return new TwistyRex       (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 10: return new TwistyMinx      (size, quat, texture, mesh, effects, moves, res, scrWidth);
       }
 
     return null;
diff --git a/src/main/java/org/distorted/objects/TwistyMinx.java b/src/main/java/org/distorted/objects/TwistyMinx.java
new file mode 100644
index 00000000..18282f19
--- /dev/null
+++ b/src/main/java/org/distorted/objects/TwistyMinx.java
@@ -0,0 +1,434 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is free software: you can redistribute it and/or modify                            //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Magic Cube is distributed in the hope that it will be useful,                                 //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objects;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshSquare;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.main.R;
+
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TwistyMinx extends TwistyObject
+{
+  private static final int FACES_PER_CUBIT =6;
+  private static final float AXIS_LEN = 1.0f/(float)Math.sqrt(2.5f+0.5f*SQ5);
+  private static final float C0 = (SQ5-1)/4;
+  private static final float C1 = (SQ5+1)/4;
+
+  // the six rotation axis of a RubikMegaminx. Must be normalized.
+  static final Static3D[] ROT_AXIS = new Static3D[]
+         {
+           new Static3D( AXIS_LEN*C1, AXIS_LEN   , 0          ),
+           new Static3D(-AXIS_LEN*C1, AXIS_LEN   , 0          ),
+           new Static3D( 0          , AXIS_LEN*C1, AXIS_LEN   ),
+           new Static3D( 0          ,-AXIS_LEN*C1, AXIS_LEN   ),
+           new Static3D( AXIS_LEN   , 0          , AXIS_LEN*C1),
+           new Static3D( AXIS_LEN   , 0          ,-AXIS_LEN*C1)
+         };
+
+  private static final int MINX_LGREEN = 0xff53aa00;
+  private static final int MINX_PINK   = 0xfffd7ab7;
+  private static final int MINX_SANDY  = 0xffefd48b;
+  private static final int MINX_LBLUE  = 0xff00a2d7;
+  private static final int MINX_ORANGE = 0xffff6200;
+  private static final int MINX_VIOLET = 0xff7d59a4;
+  private static final int MINX_DGREEN = 0xff007a47;
+  private static final int MINX_DRED   = 0xffbd0000;
+  private static final int MINX_DBLUE  = 0xff1a29b2;
+  private static final int MINX_DYELLOW= 0xffffc400;
+  private static final int MINX_WHITE  = 0xffffffff;
+  private static final int MINX_GREY   = 0xff727c7b;
+
+  private static final int[] FACE_COLORS = new int[]
+         {
+           MINX_LGREEN, MINX_PINK   , MINX_SANDY , MINX_LBLUE,
+           MINX_ORANGE, MINX_VIOLET , MINX_DGREEN, MINX_DRED ,
+           MINX_DBLUE , MINX_DYELLOW, MINX_WHITE , MINX_GREY
+         };
+
+  // All 60 legal rotation quats of a RubikMegaminx
+  private static final Static4D[] QUATS = new Static4D[]
+         {
+           new Static4D(  0.0f,  0.0f,  0.0f,  1.0f ),
+           new Static4D(  1.0f,  0.0f,  0.0f,  0.0f ),
+           new Static4D(  0.0f,  1.0f,  0.0f,  0.0f ),
+           new Static4D(  0.0f,  0.0f,  1.0f,  0.0f ),
+
+           new Static4D(  0.5f,  0.5f,  0.5f,  0.5f ),
+           new Static4D(  0.5f,  0.5f, -0.5f,  0.5f ),
+           new Static4D(  0.5f, -0.5f,  0.5f,  0.5f ),
+           new Static4D(  0.5f, -0.5f, -0.5f,  0.5f ),
+           new Static4D( -0.5f,  0.5f,  0.5f,  0.5f ),
+           new Static4D( -0.5f,  0.5f, -0.5f,  0.5f ),
+           new Static4D( -0.5f, -0.5f,  0.5f,  0.5f ),
+           new Static4D( -0.5f, -0.5f, -0.5f,  0.5f ),
+
+           new Static4D(  0.5f,    C1,    C0,  0.0f ),
+           new Static4D(  0.5f,    C1,   -C0,  0.0f ),
+           new Static4D(  0.5f,   -C1,    C0,  0.0f ),
+           new Static4D(  0.5f,   -C1,   -C0,  0.0f ),
+           new Static4D(    C0,  0.5f,    C1,  0.0f ),
+           new Static4D(    C0,  0.5f,   -C1,  0.0f ),
+           new Static4D(   -C0,  0.5f,    C1,  0.0f ),
+           new Static4D(   -C0,  0.5f,   -C1,  0.0f ),
+           new Static4D(    C1,    C0,  0.5f,  0.0f ),
+           new Static4D(    C1,   -C0,  0.5f,  0.0f ),
+           new Static4D(   -C1,    C0,  0.5f,  0.0f ),
+           new Static4D(   -C1,   -C0,  0.5f,  0.0f ),
+
+           new Static4D(  0.0f,    C0,    C1,  0.5f ),
+           new Static4D(  0.0f,    C0,   -C1,  0.5f ),
+           new Static4D(  0.0f,   -C0,    C1,  0.5f ),
+           new Static4D(  0.0f,   -C0,   -C1,  0.5f ),
+           new Static4D(    C0,    C1,  0.0f,  0.5f ),
+           new Static4D(    C0,   -C1,  0.0f,  0.5f ),
+           new Static4D(   -C0,    C1,  0.0f,  0.5f ),
+           new Static4D(   -C0,   -C1,  0.0f,  0.5f ),
+           new Static4D(    C1,  0.0f,    C0,  0.5f ),
+           new Static4D(    C1,  0.0f,   -C0,  0.5f ),
+           new Static4D(   -C1,  0.0f,    C0,  0.5f ),
+           new Static4D(   -C1,  0.0f,   -C0,  0.5f ),
+
+           new Static4D(  0.0f,    C1,  0.5f,    C0 ),
+           new Static4D(  0.0f,    C1, -0.5f,    C0 ),
+           new Static4D(  0.0f,   -C1,  0.5f,    C0 ),
+           new Static4D(  0.0f,   -C1, -0.5f,    C0 ),
+           new Static4D(  0.5f,  0.0f,    C1,    C0 ),
+           new Static4D(  0.5f,  0.0f,   -C1,    C0 ),
+           new Static4D( -0.5f,  0.0f,    C1,    C0 ),
+           new Static4D( -0.5f,  0.0f,   -C1,    C0 ),
+           new Static4D(    C1,  0.5f,  0.0f,    C0 ),
+           new Static4D(    C1, -0.5f,  0.0f,    C0 ),
+           new Static4D(   -C1,  0.5f,  0.0f,    C0 ),
+           new Static4D(   -C1, -0.5f,  0.0f,    C0 ),
+
+           new Static4D(  0.0f,  0.5f,    C0,    C1 ),
+           new Static4D(  0.0f,  0.5f,   -C0,    C1 ),
+           new Static4D(  0.0f, -0.5f,    C0,    C1 ),
+           new Static4D(  0.0f, -0.5f,   -C0,    C1 ),
+           new Static4D(  0.5f,    C0,  0.0f,    C1 ),
+           new Static4D(  0.5f,   -C0,  0.0f,    C1 ),
+           new Static4D( -0.5f,    C0,  0.0f,    C1 ),
+           new Static4D( -0.5f,   -C0,  0.0f,    C1 ),
+           new Static4D(    C0,  0.0f,  0.5f,    C1 ),
+           new Static4D(    C0,  0.0f, -0.5f,    C1 ),
+           new Static4D(   -C0,  0.0f,  0.5f,    C1 ),
+           new Static4D(   -C0,  0.0f, -0.5f,    C1 ),
+         };
+
+  private static final int[][] mFaceMap =
+         {
+           {  0, 1, 2, 12,12,12 },
+           {  2, 3, 4, 12,12,12 },
+           {  3, 4, 5, 12,12,12 },
+           {  4, 5, 6, 12,12,12 },
+           {  5, 6, 7, 12,12,12 },
+           {  6, 7, 8, 12,12,12 },
+           {  7, 8, 9, 12,12,12 },
+           { 10,11, 0, 12,12,12 },
+           { 11, 0, 1, 12,12,12 },
+           {  0, 1, 2, 12,12,12 },
+           {  0, 1, 2, 12,12,12 },
+           {  2, 3, 4, 12,12,12 },
+           {  3, 4, 5, 12,12,12 },
+           {  4, 5, 6, 12,12,12 },
+           {  5, 6, 7, 12,12,12 },
+           {  6, 7, 8, 12,12,12 },
+           {  7, 8, 9, 12,12,12 },
+           { 10,11, 0, 12,12,12 },
+           { 11, 0, 1, 12,12,12 },
+           {  0, 1, 2, 12,12,12 },
+         };
+
+  private static MeshBase mMesh;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TwistyMinx(int size, Static4D quat, DistortedTexture texture,
+             MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
+    {
+    super(size, size, 60, quat, texture, mesh, effects, moves, ObjectList.MINX, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float getScreenRatio()
+    {
+    return 1.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static4D[] getQuats()
+    {
+    return QUATS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumFaces()
+    {
+    return FACE_COLORS.length;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean shouldResetTextureMaps()
+    {
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumStickerTypes()
+    {
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getCuts(int numLayers)
+    {
+    return new float[] { -0.5f , 0.5f };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitFaces()
+    {
+    return FACES_PER_CUBIT;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getCubitPositions(int numLayers)
+    {
+    final float C0 = (SQ5+1)/4;
+    final float C1 = (SQ5+3)/4;
+
+    final Static3D[] CENTERS = new Static3D[20];
+
+    CENTERS[ 0] = new Static3D( 0.0f, 0.5f,   C1);
+    CENTERS[ 1] = new Static3D( 0.0f, 0.5f,  -C1);
+    CENTERS[ 2] = new Static3D( 0.0f,-0.5f,   C1);
+    CENTERS[ 3] = new Static3D( 0.0f,-0.5f,  -C1);
+    CENTERS[ 4] = new Static3D(   C1, 0.0f, 0.5f);
+    CENTERS[ 5] = new Static3D(   C1, 0.0f,-0.5f);
+    CENTERS[ 6] = new Static3D(  -C1, 0.0f, 0.5f);
+    CENTERS[ 7] = new Static3D(  -C1, 0.0f,-0.5f);
+    CENTERS[ 8] = new Static3D( 0.5f,   C1, 0.0f);
+    CENTERS[ 9] = new Static3D( 0.5f,  -C1, 0.0f);
+    CENTERS[10] = new Static3D(-0.5f,   C1, 0.0f);
+    CENTERS[11] = new Static3D(-0.5f,  -C1, 0.0f);
+    CENTERS[12] = new Static3D(   C0,   C0,   C0);
+    CENTERS[13] = new Static3D(   C0,   C0,  -C0);
+    CENTERS[14] = new Static3D(   C0,  -C0,   C0);
+    CENTERS[15] = new Static3D(   C0,  -C0,  -C0);
+    CENTERS[16] = new Static3D(  -C0,   C0,   C0);
+    CENTERS[17] = new Static3D(  -C0,   C0,  -C0);
+    CENTERS[18] = new Static3D(  -C0,  -C0,   C0);
+    CENTERS[19] = new Static3D(  -C0,  -C0,  -C0);
+
+    return CENTERS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getQuat(int cubit)
+    {
+    switch(cubit)
+      {
+      case  0: return 0;
+      case  1: return 2;
+      case  2: return 3;
+      case  3: return 1;
+      case  4: return 43;
+      case  5: return 9;
+      case  6: return 42;
+      case  7: return 10;
+      case  8: return 36;
+      case  9: return 5;
+      case 10: return 39;
+      case 11: return 6;
+      case 12: return 59;
+      case 13: return 32;
+      case 14: return 47;
+      case 15: return 21;
+      case 16: return 58;
+      case 17: return 34;
+      case 18: return 46;
+      case 19: return 23;
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCubitMesh(int cubit)
+    {
+    if( mMesh==null ) mMesh = FactoryCubit.getInstance().createMinxCornerMesh();
+    MeshBase mesh = mMesh.copy(true);
+
+    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[getQuat(cubit)], new Static3D(0,0,0) );
+    mesh.apply(quat,0xffffffff,0);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getFaceColor(int cubit, int cubitface, int numLayers)
+    {
+    return mFaceMap[cubit][cubitface];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
+    {
+    float S = 0.08f;
+    float R = 0.12f;
+
+    float[] vertices = {  };   // TODO
+
+    FactorySticker factory = FactorySticker.getInstance();
+    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float returnMultiplier()
+    {
+    return 2.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances()
+    {
+    return new float[] { 0.5f, 0.5f, 1.0f };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getBasicAngle()
+    {
+    return 5;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
+    {
+    int numAxis = ROTATION_AXIS.length;
+
+    if( oldRotAxis == START_AXIS )
+      {
+      return rnd.nextInt(numAxis);
+      }
+    else
+      {
+      int newVector = rnd.nextInt(numAxis-1);
+      return (newVector>=oldRotAxis ? newVector+1 : newVector);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
+    {
+    float rowFloat = rnd.nextFloat();
+
+    for(int row=0; row<mRowChances.length; row++)
+      {
+      if( rowFloat<=mRowChances[row] ) return row;
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// The Kilominx is solved if and only if:
+//
+// all cubits are rotated with the same quat.
+
+  public boolean isSolved()
+    {
+    int q = CUBITS[0].mQuatIndex;
+
+    for(int i=0; i<NUM_CUBITS; i++)
+      {
+      if( CUBITS[i].mQuatIndex != q ) return false;
+      }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// only needed for solvers - there are no Ivy solvers ATM)
+
+  public String retObjectString()
+    {
+    return "";
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getObjectName(int numLayers)
+    {
+    return R.string.minx2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getInventor(int numLayers)
+    {
+    return R.string.minx2_inventor;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getComplexity(int numLayers)
+    {
+    return 3;
+    }
+}
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index c6f94c41..8ca73a92 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -72,6 +72,7 @@ public abstract class TwistyObject extends DistortedNode
 
   static final float SQ2 = (float)Math.sqrt(2);
   static final float SQ3 = (float)Math.sqrt(3);
+  static final float SQ5 = (float)Math.sqrt(5);
   static final float SQ6 = (float)Math.sqrt(6);
 
   private static final float NODE_RATIO = 1.40f;
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index bebcec04..d42c65d5 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -87,6 +87,9 @@
     <string name="rex3"  translatable="false">Rex Cube</string>
     <string name="dino3" translatable="false">Dino Cube (6 color)</string>
     <string name="din43" translatable="false">Dino Cube (4 color)</string>
+    <string name="minx2" translatable="false">Kilominx</string>
+    <string name="minx3" translatable="false">Megaminx</string>
+    <string name="minx4" translatable="false">Gigaminx</string>
 
     <string name="cube2_inventor" translatable="false">Larry Nichols, 1970</string>
     <string name="cube3_inventor" translatable="false">Ernő Rubik, 1974</string>
@@ -104,5 +107,8 @@
     <string name="rex3_inventor"  translatable="false">Andrew Cormier, 2009</string>
     <string name="dino3_inventor" translatable="false">Robert Webb, 1985</string>
     <string name="din43_inventor" translatable="false">Robert Webb, 1985</string>
+    <string name="minx2_inventor" translatable="false">Thomas de Bruin, 2008</string>
+    <string name="minx3_inventor" translatable="false">Ferenc Szlivka, 1982</string>
+    <string name="minx4_inventor" translatable="false">Tyler Fox, 2006</string>
 
 </resources>
