commit a64e07d016fec6656f68e7d6b8e57de3c22baf96
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Jan 19 17:17:23 2021 +0100

    Beginnings of Megaminx & Gigaminx

diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index d21e9fa5..c21b4d47 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -80,8 +80,8 @@ public class RubikSurfaceView extends GLSurfaceView
     private int mFirstIndex, mLastIndex;
     private int mDensity;
 
-    private static Static4D mQuat= new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
-    private static Static4D mTemp= new Static4D(0,0,0,1);
+    private static final Static4D mQuat= new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
+    private static final Static4D mTemp= new Static4D(0,0,0,1);
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/main/java/org/distorted/objects/FactoryCubit.java b/src/main/java/org/distorted/objects/FactoryCubit.java
index 08679ea4..2ce4f1a7 100644
--- a/src/main/java/org/distorted/objects/FactoryCubit.java
+++ b/src/main/java/org/distorted/objects/FactoryCubit.java
@@ -31,6 +31,8 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import static org.distorted.objects.TwistyMegaminx.MEGA_D;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class FactoryCubit
@@ -48,10 +50,10 @@ class FactoryCubit
   static final float SIN54   = (SQ5+1)/4;                         // sin(54 deg)
   static final float COS54   = (float)(Math.sqrt(10-2*SQ5)/4);    // cos(54 deg)
 
-  static final float MINX_C0 = (SQ5-1)/4;
-  static final float MINX_C4 = (float)(Math.sqrt(0.5f-0.1f*SQ5)); // cos(half the dihedral angle)
-  static final float MINX_C5 = (float)(Math.sqrt(0.5f+0.1f*SQ5)); // sin(half the dihedral angle)
-  static final float MINX_SC = 0.5f;
+  static final float MINX_C0  = (SQ5-1)/4;
+  static final float COS_HALFD= (float)(Math.sqrt(0.5f-0.1f*SQ5)); // cos(half the dihedral angle)
+  static final float SIN_HALFD= (float)(Math.sqrt(0.5f+0.1f*SQ5)); // sin(half the dihedral angle)
+  static final float MINX_SC  = 0.5f;
 
   private static final int IVY_N = 8;
 
@@ -686,7 +688,7 @@ class FactoryCubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createFacesMinxCorner()
+  MeshBase createFacesKilominxCorner()
     {
     MeshBase[] meshes = new MeshPolygon[6];
 
@@ -694,9 +696,9 @@ class FactoryCubit
     float Y1= (float)(Math.sqrt(2+0.4f*SQ5)/4);
     float Y2= Y1 - (float)(Math.sqrt(10-2*SQ5)/8);
     float H = 0.5f* SIN54 /COS54  ;
-    float X2= MINX_SC*H*MINX_C5;
-    float Y3= MINX_SC*H/(2*MINX_C4);
-    float Y4= MINX_SC*H*(1/(2*MINX_C4) - MINX_C4);
+    float X2= MINX_SC*H* SIN_HALFD;
+    float Y3= MINX_SC*H/(2*COS_HALFD);
+    float Y4= MINX_SC*H*(1/(2*COS_HALFD) - COS_HALFD);
 
     float[] vertices0 = { -X1, Y2, 0, -Y1, X1, Y2, 0, Y1 };
     float[] bands0 = computeBands(0.04f,17,0.3f,0.2f,5);
@@ -719,6 +721,34 @@ class FactoryCubit
     return new MeshJoined(meshes);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createFacesMegaminxCorner()
+    {
+    MeshBase[] meshes = new MeshPolygon[6];
+
+    float Y = COS54/(2*SIN54);
+
+    float[] vertices0 = { -0.5f, 0.0f, 0.0f, -Y, 0.5f, 0.0f, 0.0f, Y };
+    float[] bands0 = computeBands(0.04f,34,0.3f,0.2f,5);
+    float[] bands1 = computeBands(0.00f,34,0.3f,0.2f,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(vertices0, bands1, 1, 4);
+    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
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1218,22 +1248,22 @@ class FactoryCubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  VertexEffect[] createVertexEffectsMinxCorner()
+  VertexEffect[] createVertexEffectsKilominxCorner()
     {
     VertexEffect[] effect = new VertexEffect[10];
 
     float H = 0.5f*(SIN54 /COS54  );
     float Y1= (float)(Math.sqrt(2+0.4f*SQ5)/4);
-    float Y2= H/(2*MINX_C4);
+    float Y2= H/(2*COS_HALFD);
     float A = (float)(Math.acos(-SQ5/5)*180/Math.PI);  // dihedral angle of a dedecahedron in degrees
     float sin18 = MINX_C0;
     float cos18 = (float)(Math.sqrt(1-MINX_C0*MINX_C0));
-    float LEN   = (float)Math.sqrt(H*H/(MINX_C4*MINX_C4) + 0.25f);
+    float LEN   = (float)Math.sqrt(H*H/(COS_HALFD*COS_HALFD) + 0.25f);
 
     Static3D axisZ = new Static3D(0.0f  , 0.0f , 1.0f);
     Static3D axisY = new Static3D(0.0f  , 1.0f , 0.0f);
     Static3D axisA = new Static3D(-sin18, cos18, 0.0f);
-    Static3D axisC = new Static3D( H/LEN, -0.5f/LEN,-H*MINX_C5/(MINX_C4*LEN));
+    Static3D axisC = new Static3D( H/LEN, -0.5f/LEN,-H* SIN_HALFD /(COS_HALFD*LEN));
 
     Static3D move1 = new Static3D(0,-Y1,0);
     Static3D move2 = new Static3D(0,-Y2,0);
@@ -1271,6 +1301,54 @@ class FactoryCubit
     return effect;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createVertexEffectsMegaminxCorner(int numLayers)
+    {
+    VertexEffect[] effect = new VertexEffect[9];
+
+    float Y = COS54/(2*SIN54);
+    float A = (float)(Math.acos(-SQ5/5)*180/Math.PI);  // dihedral angle of a dedecahedron in degrees
+
+    float sinA = (2*SIN54*SIN54-1)/COS54;
+    float cosA = (float)Math.sqrt(1-sinA*sinA);
+    float LEN  = 0.5f/SIN54;
+    float scale= 2*SIN54*(0.5f-MEGA_D)/(0.5f*(numLayers-1));
+
+    Static3D axisA = new Static3D( SIN54, COS54, 0.0f);
+    Static3D axisB = new Static3D(-SIN54, COS54, 0.0f);
+    Static3D axisX = new Static3D(  1.0f,  0.0f, 0.0f);
+
+    Static3D centerU = new Static3D( 0.0f, Y, 0.0f);
+    Static3D centerD = new Static3D( 0.0f,-Y, 0.0f);
+
+    Static3D move1= new Static3D(0.0f, -sinA*LEN, -cosA*LEN );
+    Static3D move2= new Static3D(0.0f, Y , 0.0f );
+
+    Static1D angleD = new Static1D(A);
+    Static1D angleE = new Static1D(360-A);
+    Static1D angleF = new Static1D( (float)((180/Math.PI)*Math.asin(sinA) - 90) );
+
+    effect[0] = new VertexEffectScale ( new Static3D( 1, 1,-1) );
+    effect[1] = new VertexEffectRotate(angleE, axisA, centerU);
+    effect[2] = new VertexEffectRotate(angleD, axisB, centerU);
+    effect[3] = new VertexEffectMove(move1);
+    effect[4] = new VertexEffectRotate(angleE, axisA, centerD);
+    effect[5] = new VertexEffectRotate(angleD, axisB, centerD);
+    effect[6] = new VertexEffectRotate(angleF, axisX, centerD);
+    effect[7] = new VertexEffectMove(move2);
+    effect[8] = new VertexEffectScale(scale);
+
+    effect[0].setMeshAssociation(  3,-1);  // meshes 0,1
+    effect[1].setMeshAssociation( 16,-1);  // mesh 4
+    effect[2].setMeshAssociation( 32,-1);  // mesh 5
+    effect[3].setMeshAssociation( 56,-1);  // meshes 3,4,5
+    effect[4].setMeshAssociation(  1,-1);  // mesh 0
+    effect[5].setMeshAssociation(  2,-1);  // mesh 1
+
+    return effect;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // OBJECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1626,17 +1704,17 @@ class FactoryCubit
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createMinxCornerMesh()
+  MeshBase createKilominxCornerMesh()
     {
-    MeshBase mesh = createFacesMinxCorner();
-    VertexEffect[] effects = createVertexEffectsMinxCorner();
+    MeshBase mesh = createFacesKilominxCorner();
+    VertexEffect[] effects = createVertexEffectsKilominxCorner();
     for( VertexEffect effect : effects ) mesh.apply(effect);
 
     float A = (2*SQ3/3)* SIN54;
     float B = 0.4f;
-    float X = MINX_C5* SIN54 *COS54  ;
+    float X = SIN_HALFD * SIN54 *COS54  ;
     float Y = SIN54 * SIN54 - 0.5f;
-    float Z = MINX_C4* SIN54 *COS54  ;
+    float Z = COS_HALFD* SIN54 *COS54  ;
 
     Static3D center = new Static3D(0.0f, -(float)Math.sqrt(1-A*A)*B,-A*B);
 
@@ -1650,6 +1728,36 @@ class FactoryCubit
 
     mesh.mergeEffComponents();
 
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createMegaminxCornerMesh(int numLayers)
+    {
+    MeshBase mesh = createFacesMegaminxCorner();
+    VertexEffect[] effects = createVertexEffectsMegaminxCorner(numLayers);
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    float A = (2*SQ3/3)* SIN54;
+    float B = 0.4f;
+    float X = SIN_HALFD* SIN54 * COS54;
+    float Y = SIN54 * SIN54 - 0.5f;
+    float Z = COS_HALFD* SIN54 * COS54;
+    float S = 2*SIN54*(0.5f-MEGA_D)/(0.5f*(numLayers-1));
+
+    Static3D center = new Static3D(0.0f, -(float)Math.sqrt(1-A*A)*B,-A*B);
+
+    Static3D[] vertices = new Static3D[4];
+    vertices[0] = new Static3D( 0.0f, 0.0f  , 0.0f);
+    vertices[1] = new Static3D( 0.0f,-0.5f*S, 0.0f);
+    vertices[2] = new Static3D(-X*S , Y*S   ,-Z*S );
+    vertices[3] = new Static3D(+X*S , Y*S   ,-Z*S );
+
+    roundCorners(mesh,center,vertices,0.04f,0.10f);
+
+    mesh.mergeEffComponents();
+
     return mesh;
     }
   }
diff --git a/src/main/java/org/distorted/objects/MovementKilominx.java b/src/main/java/org/distorted/objects/MovementKilominx.java
deleted file mode 100644
index 28e5c633..00000000
--- a/src/main/java/org/distorted/objects/MovementKilominx.java
+++ /dev/null
@@ -1,303 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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;
-import static org.distorted.objects.TwistyKilominx.C1;
-import static org.distorted.objects.TwistyKilominx.C2;
-import static org.distorted.objects.TwistyKilominx.LEN;
-import static org.distorted.objects.FactoryCubit.SIN54;
-import static org.distorted.objects.FactoryCubit.COS54;
-import static org.distorted.objects.TwistyObject.SQ5;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class MovementKilominx extends Movement
-{
-  static final float DIST3D = (float)Math.sqrt(0.625f+0.275f*SQ5)/3;
-  static final float DIST2D = (0.5f*SIN54/COS54)/3;
-
-  static final Static3D[] FACE_AXIS = new Static3D[]
-         {
-           new Static3D( C2/LEN, C1/LEN, 0      ),
-           new Static3D( C2/LEN,-C1/LEN, 0      ),
-           new Static3D(-C2/LEN, C1/LEN, 0      ),
-           new Static3D(-C2/LEN,-C1/LEN, 0      ),
-           new Static3D( 0     , C2/LEN, C1/LEN ),
-           new Static3D( 0     , C2/LEN,-C1/LEN ),
-           new Static3D( 0     ,-C2/LEN, C1/LEN ),
-           new Static3D( 0     ,-C2/LEN,-C1/LEN ),
-           new Static3D( C1/LEN, 0     , C2/LEN ),
-           new Static3D( C1/LEN, 0     ,-C2/LEN ),
-           new Static3D(-C1/LEN, 0     , C2/LEN ),
-           new Static3D(-C1/LEN, 0     ,-C2/LEN )
-         };
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MovementKilominx()
-    {
-    super(TwistyKilominx.ROT_AXIS, FACE_AXIS, DIST3D, DIST2D);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int computeRowFromOffset(int face, int size, float offset)
-    {
-    return offset<DIST2D ? 0:2;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float returnRotationFactor(int size, int row)
-    {
-    return 1.0f;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return angle (in radians) that the line connecting the center C of the pentagonal face and the
-// first vertex of the pentagon makes with a vertical line coming upwards from the center C.
-
-  private float returnAngle(int face)
-    {
-    switch(face)
-      {
-      case  0:
-      case  2:
-      case  6:
-      case  7: return 0.0f;
-      case  1:
-      case  3:
-      case  4:
-      case  5: return (float)(36*Math.PI/180);
-      case  9:
-      case 10: return (float)(54*Math.PI/180);
-      case  8:
-      case 11: return (float)(18*Math.PI/180);
-      }
-
-    return 0.0f;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The pair (distance,angle) defines a point P in R^2 in polar coordinate system. Let V be the vector
-// from the center of the coordinate system to P.
-// Let P' be the point defined by polar (distance,angle+PI/2). Let Lh be the half-line starting at
-// P' and going in the direction of V.
-// Return true iff point 'point' lies on the left of Lh, i.e. when we rotate (using the center of
-// the coordinate system as the center of rotation) 'point' and Lh in such a way that Lh points
-// directly upwards, is 'point' on the left or the right of it?
-
-  private boolean isOnTheLeft(float[] point, float distance, float angle)
-    {
-    float sin = (float)Math.sin(angle);
-    float cos = (float)Math.cos(angle);
-
-    float vx = point[0] + sin*distance;
-    float vy = point[1] - cos*distance;
-
-    return vx*sin < vy*cos;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Return 1,2,3,4,5 - the vertex of the pentagon to which point 'point' is the closest, if the
-// 'point' is inside the pentagon - or 0 otherwise.
-// The 'first' vertex is the one we meet the first when we rotate clockwise starting from 12:00.
-// This vertex makes angle 'returnAngle()' with the line coming out upwards from the center of the
-// pentagon.
-// Distance from the center to a vertex of the pentagon = 1/(6*COS54)
-
-  private int returnPartOfThePentagon(float[] point, int face)
-    {
-    float angle = returnAngle(face);
-    float A = (float)(Math.PI/5);
-
-    for(int i=0; i<5; i++)
-      {
-      if( isOnTheLeft(point, DIST2D, (9-2*i)*A-angle) ) return 0;
-      }
-
-    if( isOnTheLeft(point, 0, 2.5f*A-angle) )
-      {
-      if( isOnTheLeft(point, 0, 3.5f*A-angle) )
-        {
-        return isOnTheLeft(point, 0, 5.5f*A-angle) ? 4 : 5;
-        }
-      else return 1;
-      }
-    else
-      {
-      if( isOnTheLeft(point, 0, 4.5f*A-angle) )
-        {
-        return 3;
-        }
-      else
-        {
-        return isOnTheLeft(point, 0, 6.5f*A-angle) ? 2 : 1;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean isInsideFace(int face, float[] p)
-    {
-    return returnPartOfThePentagon(p,face) > 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void computeEnabledAxis(int face, float[] touchPoint, int[] enabled)
-    {
-    int part = returnPartOfThePentagon(touchPoint,face);
-
-    if( part>0 )
-      {
-      enabled[0] = 2;
-
-      switch(face)
-        {
-        case  0:  switch(part)
-                    {
-                    case 1: enabled[1]=2; enabled[2]=3; break;
-                    case 2: enabled[1]=3; enabled[2]=5; break;
-                    case 3: enabled[1]=5; enabled[2]=1; break;
-                    case 4: enabled[1]=1; enabled[2]=4; break;
-                    case 5: enabled[1]=4; enabled[2]=2; break;
-                    }
-                  break;
-
-        case  1:  switch(part)
-                    {
-                    case 1: enabled[1]=0; enabled[2]=5; break;
-                    case 2: enabled[1]=5; enabled[2]=2; break;
-                    case 3: enabled[1]=2; enabled[2]=3; break;
-                    case 4: enabled[1]=3; enabled[2]=4; break;
-                    case 5: enabled[1]=4; enabled[2]=0; break;
-                    }
-                  break;
-
-        case  2:  switch(part)
-                    {
-                    case 1: enabled[1]=3; enabled[2]=2; break;
-                    case 2: enabled[1]=2; enabled[2]=5; break;
-                    case 3: enabled[1]=5; enabled[2]=0; break;
-                    case 4: enabled[1]=0; enabled[2]=4; break;
-                    case 5: enabled[1]=4; enabled[2]=3; break;
-                    }
-                  break;
-
-        case  3:  switch(part)
-                    {
-                    case 1: enabled[1]=1; enabled[2]=5; break;
-                    case 2: enabled[1]=5; enabled[2]=3; break;
-                    case 3: enabled[1]=3; enabled[2]=2; break;
-                    case 4: enabled[1]=2; enabled[2]=4; break;
-                    case 5: enabled[1]=4; enabled[2]=1; break;
-                    }
-                  break;
-
-        case  4:  switch(part)
-                    {
-                    case 1: enabled[1]=3; enabled[2]=0; break;
-                    case 2: enabled[1]=0; enabled[2]=4; break;
-                    case 3: enabled[1]=4; enabled[2]=5; break;
-                    case 4: enabled[1]=5; enabled[2]=1; break;
-                    case 5: enabled[1]=1; enabled[2]=3; break;
-                    }
-                  break;
-
-        case  5:  switch(part)
-                    {
-                    case 1: enabled[1]=2; enabled[2]=1; break;
-                    case 2: enabled[1]=1; enabled[2]=4; break;
-                    case 3: enabled[1]=4; enabled[2]=5; break;
-                    case 4: enabled[1]=5; enabled[2]=0; break;
-                    case 5: enabled[1]=0; enabled[2]=2; break;
-                    }
-                  break;
-
-        case  6:  switch(part)
-                    {
-                    case 1: enabled[1]=5; enabled[2]=4; break;
-                    case 2: enabled[1]=4; enabled[2]=1; break;
-                    case 3: enabled[1]=1; enabled[2]=2; break;
-                    case 4: enabled[1]=2; enabled[2]=0; break;
-                    case 5: enabled[1]=0; enabled[2]=5; break;
-                    }
-                  break;
-
-        case  7:  switch(part)
-                    {
-                    case 1: enabled[1]=5; enabled[2]=4; break;
-                    case 2: enabled[1]=4; enabled[2]=0; break;
-                    case 3: enabled[1]=0; enabled[2]=3; break;
-                    case 4: enabled[1]=3; enabled[2]=1; break;
-                    case 5: enabled[1]=1; enabled[2]=5; break;
-                    }
-                  break;
-
-        case  8: switch(part)
-                    {
-                    case 1: enabled[1]=2; enabled[2]=0; break;
-                    case 2: enabled[1]=0; enabled[2]=1; break;
-                    case 3: enabled[1]=1; enabled[2]=3; break;
-                    case 4: enabled[1]=3; enabled[2]=5; break;
-                    case 5: enabled[1]=5; enabled[2]=2; break;
-                    }
-                  break;
-
-        case  9:  switch(part)
-                    {
-                    case 1: enabled[1]=3; enabled[2]=4; break;
-                    case 2: enabled[1]=4; enabled[2]=2; break;
-                    case 3: enabled[1]=2; enabled[2]=1; break;
-                    case 4: enabled[1]=1; enabled[2]=0; break;
-                    case 5: enabled[1]=0; enabled[2]=3; break;
-                    }
-                  break;
-
-        case 10:  switch(part)
-                    {
-                    case 1: enabled[1]=2; enabled[2]=4; break;
-                    case 2: enabled[1]=4; enabled[2]=3; break;
-                    case 3: enabled[1]=3; enabled[2]=0; break;
-                    case 4: enabled[1]=0; enabled[2]=1; break;
-                    case 5: enabled[1]=1; enabled[2]=2; break;
-                    }
-                  break;
-
-        case 11:  switch(part)
-                    {
-                    case 1: enabled[1]=3; enabled[2]=1; break;
-                    case 2: enabled[1]=1; enabled[2]=0; break;
-                    case 3: enabled[1]=0; enabled[2]=2; break;
-                    case 4: enabled[1]=2; enabled[2]=5; break;
-                    case 5: enabled[1]=5; enabled[2]=3; break;
-                    }
-                  break;
-        }
-      }
-    else
-      {
-      enabled[0] = 0;
-      }
-    }
-}
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..b18143c4
--- /dev/null
+++ b/src/main/java/org/distorted/objects/MovementMinx.java
@@ -0,0 +1,303 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+import static org.distorted.objects.TwistyKilominx.C1;
+import static org.distorted.objects.TwistyKilominx.C2;
+import static org.distorted.objects.TwistyKilominx.LEN;
+import static org.distorted.objects.FactoryCubit.SIN54;
+import static org.distorted.objects.FactoryCubit.COS54;
+import static org.distorted.objects.TwistyObject.SQ5;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class MovementMinx extends Movement
+{
+  static final float DIST3D = (float)Math.sqrt(0.625f+0.275f*SQ5)/3;
+  static final float DIST2D = (0.5f*SIN54/COS54)/3;
+
+  static final Static3D[] FACE_AXIS = new Static3D[]
+         {
+           new Static3D( C2/LEN, C1/LEN, 0      ),
+           new Static3D( C2/LEN,-C1/LEN, 0      ),
+           new Static3D(-C2/LEN, C1/LEN, 0      ),
+           new Static3D(-C2/LEN,-C1/LEN, 0      ),
+           new Static3D( 0     , C2/LEN, C1/LEN ),
+           new Static3D( 0     , C2/LEN,-C1/LEN ),
+           new Static3D( 0     ,-C2/LEN, C1/LEN ),
+           new Static3D( 0     ,-C2/LEN,-C1/LEN ),
+           new Static3D( C1/LEN, 0     , C2/LEN ),
+           new Static3D( C1/LEN, 0     ,-C2/LEN ),
+           new Static3D(-C1/LEN, 0     , C2/LEN ),
+           new Static3D(-C1/LEN, 0     ,-C2/LEN )
+         };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MovementMinx()
+    {
+    super(TwistyKilominx.ROT_AXIS, FACE_AXIS, DIST3D, DIST2D);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int computeRowFromOffset(int face, int size, float offset)
+    {
+    return offset<DIST2D ? 0:2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float returnRotationFactor(int size, int row)
+    {
+    return 1.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return angle (in radians) that the line connecting the center C of the pentagonal face and the
+// first vertex of the pentagon makes with a vertical line coming upwards from the center C.
+
+  private float returnAngle(int face)
+    {
+    switch(face)
+      {
+      case  0:
+      case  2:
+      case  6:
+      case  7: return 0.0f;
+      case  1:
+      case  3:
+      case  4:
+      case  5: return (float)(36*Math.PI/180);
+      case  9:
+      case 10: return (float)(54*Math.PI/180);
+      case  8:
+      case 11: return (float)(18*Math.PI/180);
+      }
+
+    return 0.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// The pair (distance,angle) defines a point P in R^2 in polar coordinate system. Let V be the vector
+// from the center of the coordinate system to P.
+// Let P' be the point defined by polar (distance,angle+PI/2). Let Lh be the half-line starting at
+// P' and going in the direction of V.
+// Return true iff point 'point' lies on the left of Lh, i.e. when we rotate (using the center of
+// the coordinate system as the center of rotation) 'point' and Lh in such a way that Lh points
+// directly upwards, is 'point' on the left or the right of it?
+
+  private boolean isOnTheLeft(float[] point, float distance, float angle)
+    {
+    float sin = (float)Math.sin(angle);
+    float cos = (float)Math.cos(angle);
+
+    float vx = point[0] + sin*distance;
+    float vy = point[1] - cos*distance;
+
+    return vx*sin < vy*cos;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Return 1,2,3,4,5 - the vertex of the pentagon to which point 'point' is the closest, if the
+// 'point' is inside the pentagon - or 0 otherwise.
+// The 'first' vertex is the one we meet the first when we rotate clockwise starting from 12:00.
+// This vertex makes angle 'returnAngle()' with the line coming out upwards from the center of the
+// pentagon.
+// Distance from the center to a vertex of the pentagon = 1/(6*COS54)
+
+  private int returnPartOfThePentagon(float[] point, int face)
+    {
+    float angle = returnAngle(face);
+    float A = (float)(Math.PI/5);
+
+    for(int i=0; i<5; i++)
+      {
+      if( isOnTheLeft(point, DIST2D, (9-2*i)*A-angle) ) return 0;
+      }
+
+    if( isOnTheLeft(point, 0, 2.5f*A-angle) )
+      {
+      if( isOnTheLeft(point, 0, 3.5f*A-angle) )
+        {
+        return isOnTheLeft(point, 0, 5.5f*A-angle) ? 4 : 5;
+        }
+      else return 1;
+      }
+    else
+      {
+      if( isOnTheLeft(point, 0, 4.5f*A-angle) )
+        {
+        return 3;
+        }
+      else
+        {
+        return isOnTheLeft(point, 0, 6.5f*A-angle) ? 2 : 1;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean isInsideFace(int face, float[] p)
+    {
+    return returnPartOfThePentagon(p,face) > 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void computeEnabledAxis(int face, float[] touchPoint, int[] enabled)
+    {
+    int part = returnPartOfThePentagon(touchPoint,face);
+
+    if( part>0 )
+      {
+      enabled[0] = 2;
+
+      switch(face)
+        {
+        case  0:  switch(part)
+                    {
+                    case 1: enabled[1]=2; enabled[2]=3; break;
+                    case 2: enabled[1]=3; enabled[2]=5; break;
+                    case 3: enabled[1]=5; enabled[2]=1; break;
+                    case 4: enabled[1]=1; enabled[2]=4; break;
+                    case 5: enabled[1]=4; enabled[2]=2; break;
+                    }
+                  break;
+
+        case  1:  switch(part)
+                    {
+                    case 1: enabled[1]=0; enabled[2]=5; break;
+                    case 2: enabled[1]=5; enabled[2]=2; break;
+                    case 3: enabled[1]=2; enabled[2]=3; break;
+                    case 4: enabled[1]=3; enabled[2]=4; break;
+                    case 5: enabled[1]=4; enabled[2]=0; break;
+                    }
+                  break;
+
+        case  2:  switch(part)
+                    {
+                    case 1: enabled[1]=3; enabled[2]=2; break;
+                    case 2: enabled[1]=2; enabled[2]=5; break;
+                    case 3: enabled[1]=5; enabled[2]=0; break;
+                    case 4: enabled[1]=0; enabled[2]=4; break;
+                    case 5: enabled[1]=4; enabled[2]=3; break;
+                    }
+                  break;
+
+        case  3:  switch(part)
+                    {
+                    case 1: enabled[1]=1; enabled[2]=5; break;
+                    case 2: enabled[1]=5; enabled[2]=3; break;
+                    case 3: enabled[1]=3; enabled[2]=2; break;
+                    case 4: enabled[1]=2; enabled[2]=4; break;
+                    case 5: enabled[1]=4; enabled[2]=1; break;
+                    }
+                  break;
+
+        case  4:  switch(part)
+                    {
+                    case 1: enabled[1]=3; enabled[2]=0; break;
+                    case 2: enabled[1]=0; enabled[2]=4; break;
+                    case 3: enabled[1]=4; enabled[2]=5; break;
+                    case 4: enabled[1]=5; enabled[2]=1; break;
+                    case 5: enabled[1]=1; enabled[2]=3; break;
+                    }
+                  break;
+
+        case  5:  switch(part)
+                    {
+                    case 1: enabled[1]=2; enabled[2]=1; break;
+                    case 2: enabled[1]=1; enabled[2]=4; break;
+                    case 3: enabled[1]=4; enabled[2]=5; break;
+                    case 4: enabled[1]=5; enabled[2]=0; break;
+                    case 5: enabled[1]=0; enabled[2]=2; break;
+                    }
+                  break;
+
+        case  6:  switch(part)
+                    {
+                    case 1: enabled[1]=5; enabled[2]=4; break;
+                    case 2: enabled[1]=4; enabled[2]=1; break;
+                    case 3: enabled[1]=1; enabled[2]=2; break;
+                    case 4: enabled[1]=2; enabled[2]=0; break;
+                    case 5: enabled[1]=0; enabled[2]=5; break;
+                    }
+                  break;
+
+        case  7:  switch(part)
+                    {
+                    case 1: enabled[1]=5; enabled[2]=4; break;
+                    case 2: enabled[1]=4; enabled[2]=0; break;
+                    case 3: enabled[1]=0; enabled[2]=3; break;
+                    case 4: enabled[1]=3; enabled[2]=1; break;
+                    case 5: enabled[1]=1; enabled[2]=5; break;
+                    }
+                  break;
+
+        case  8: switch(part)
+                    {
+                    case 1: enabled[1]=2; enabled[2]=0; break;
+                    case 2: enabled[1]=0; enabled[2]=1; break;
+                    case 3: enabled[1]=1; enabled[2]=3; break;
+                    case 4: enabled[1]=3; enabled[2]=5; break;
+                    case 5: enabled[1]=5; enabled[2]=2; break;
+                    }
+                  break;
+
+        case  9:  switch(part)
+                    {
+                    case 1: enabled[1]=3; enabled[2]=4; break;
+                    case 2: enabled[1]=4; enabled[2]=2; break;
+                    case 3: enabled[1]=2; enabled[2]=1; break;
+                    case 4: enabled[1]=1; enabled[2]=0; break;
+                    case 5: enabled[1]=0; enabled[2]=3; break;
+                    }
+                  break;
+
+        case 10:  switch(part)
+                    {
+                    case 1: enabled[1]=2; enabled[2]=4; break;
+                    case 2: enabled[1]=4; enabled[2]=3; break;
+                    case 3: enabled[1]=3; enabled[2]=0; break;
+                    case 4: enabled[1]=0; enabled[2]=1; break;
+                    case 5: enabled[1]=1; enabled[2]=2; break;
+                    }
+                  break;
+
+        case 11:  switch(part)
+                    {
+                    case 1: enabled[1]=3; enabled[2]=1; break;
+                    case 2: enabled[1]=1; enabled[2]=0; break;
+                    case 3: enabled[1]=0; enabled[2]=2; break;
+                    case 4: enabled[1]=2; enabled[2]=5; break;
+                    case 5: enabled[1]=5; enabled[2]=3; break;
+                    }
+                  break;
+        }
+      }
+    else
+      {
+      enabled[0] = 0;
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objects/ObjectList.java b/src/main/java/org/distorted/objects/ObjectList.java
index fac6fa53..529963c3 100644
--- a/src/main/java/org/distorted/objects/ObjectList.java
+++ b/src/main/java/org/distorted/objects/ObjectList.java
@@ -135,7 +135,17 @@ public enum ObjectList
                        {3 , 18, R.raw.kilo3, R.drawable.ui_small_kilo3, R.drawable.ui_medium_kilo3, R.drawable.ui_big_kilo3, R.drawable.ui_huge_kilo3} ,
                      },
          TwistyKilominx.class,
-         new MovementKilominx(),
+         new MovementMinx(),
+         4
+       ),
+
+  MEGA (
+         new int[][] {
+                       {3 , 21, R.raw.kilo3, R.drawable.ui_small_kilo3, R.drawable.ui_medium_kilo3, R.drawable.ui_big_kilo3, R.drawable.ui_huge_kilo3} ,
+                       {5 , 25, R.raw.kilo3, R.drawable.ui_small_kilo3, R.drawable.ui_medium_kilo3, R.drawable.ui_big_kilo3, R.drawable.ui_huge_kilo3} ,
+                     },
+         TwistyMegaminx.class,
+         new MovementMinx(),
          4
        ),
   ;
@@ -521,6 +531,7 @@ public enum ObjectList
       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 TwistyKilominx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 11: return new TwistyMegaminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
       }
 
     return null;
diff --git a/src/main/java/org/distorted/objects/TwistyCube.java b/src/main/java/org/distorted/objects/TwistyCube.java
index d97ebe97..838556dd 100644
--- a/src/main/java/org/distorted/objects/TwistyCube.java
+++ b/src/main/java/org/distorted/objects/TwistyCube.java
@@ -104,7 +104,7 @@ class TwistyCube extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     if( mMeshes==null )
       {
@@ -179,13 +179,13 @@ class TwistyCube extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getCuts(int size)
+  float[] getCuts(int numLayers)
     {
-    float[] cuts = new float[size-1];
+    float[] cuts = new float[numLayers-1];
 
-    for(int i=0; i<size-1; i++)
+    for(int i=0; i<numLayers-1; i++)
       {
-      cuts[i] = (2-size)*0.5f + i;
+      cuts[i] = (2-numLayers)*0.5f + i;
       }
 
     return cuts;
@@ -193,7 +193,7 @@ class TwistyCube extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -229,9 +229,8 @@ class TwistyCube extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
-    int numLayers = getNumLayers();
     float[] chances = new float[numLayers];
 
     for(int i=0; i<numLayers; i++)
diff --git a/src/main/java/org/distorted/objects/TwistyDiamond.java b/src/main/java/org/distorted/objects/TwistyDiamond.java
index b4adea15..02cb7c66 100644
--- a/src/main/java/org/distorted/objects/TwistyDiamond.java
+++ b/src/main/java/org/distorted/objects/TwistyDiamond.java
@@ -160,7 +160,7 @@ public class TwistyDiamond extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -215,7 +215,7 @@ public class TwistyDiamond extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
@@ -266,7 +266,7 @@ public class TwistyDiamond extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     float[] chances = new float[2];
 
diff --git a/src/main/java/org/distorted/objects/TwistyDino.java b/src/main/java/org/distorted/objects/TwistyDino.java
index 7a6039fb..fc1edf59 100644
--- a/src/main/java/org/distorted/objects/TwistyDino.java
+++ b/src/main/java/org/distorted/objects/TwistyDino.java
@@ -170,7 +170,7 @@ public abstract class TwistyDino extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -191,7 +191,7 @@ public abstract class TwistyDino extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     if( mMesh==null ) mMesh = FactoryCubit.getInstance().createDinoMesh();
 
@@ -224,7 +224,7 @@ public abstract class TwistyDino extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     float[] chances = new float[3];
 
diff --git a/src/main/java/org/distorted/objects/TwistyHelicopter.java b/src/main/java/org/distorted/objects/TwistyHelicopter.java
index 397106df..e873816f 100644
--- a/src/main/java/org/distorted/objects/TwistyHelicopter.java
+++ b/src/main/java/org/distorted/objects/TwistyHelicopter.java
@@ -182,7 +182,7 @@ public class TwistyHelicopter extends TwistyObject
            { 5 , 6,6,6,6,6 },
          };
 
-  private static int[] QUAT_INDICES =
+  private static final int[] QUAT_INDICES =
       { 0,13,14,1,12,2,3,7,20,6,13,17,7,23,18,12,22,10,8,16,11,21,19,9,3,15,14,0,5,2,1,4 };
 
   private static MeshBase mCornerMesh, mFaceMesh;
@@ -225,7 +225,7 @@ public class TwistyHelicopter extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -258,7 +258,7 @@ public class TwistyHelicopter extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
@@ -309,7 +309,7 @@ public class TwistyHelicopter extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     float[] chances = new float[3];
 
diff --git a/src/main/java/org/distorted/objects/TwistyIvy.java b/src/main/java/org/distorted/objects/TwistyIvy.java
index 8fc55de0..b341dc42 100644
--- a/src/main/java/org/distorted/objects/TwistyIvy.java
+++ b/src/main/java/org/distorted/objects/TwistyIvy.java
@@ -132,7 +132,7 @@ public class TwistyIvy extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 2;
     }
@@ -201,7 +201,7 @@ public class TwistyIvy extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
@@ -257,7 +257,7 @@ public class TwistyIvy extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     float[] chances = new float[2];
     chances[0] = 0.5f;
diff --git a/src/main/java/org/distorted/objects/TwistyKilominx.java b/src/main/java/org/distorted/objects/TwistyKilominx.java
index 07849f63..f7188986 100644
--- a/src/main/java/org/distorted/objects/TwistyKilominx.java
+++ b/src/main/java/org/distorted/objects/TwistyKilominx.java
@@ -32,187 +32,23 @@ 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 TwistyKilominx extends TwistyObject
+public class TwistyKilominx extends TwistyMinx
 {
-  private static final int FACES_PER_CUBIT =6;
-
-  static final float C0 = (SQ5-1)/4;                       // cos(72 deg)
-  static final float C1 = (SQ5+1)/4;                       // cos(36 deg)
-  static final float C2 = (SQ5+3)/4;
-  static final float LEN= (float)(Math.sqrt(1.25f+0.5f*SQ5));
-
-  // the six rotation axis of a Kilominx. Must be normalized.
-  static final Static3D[] ROT_AXIS = new Static3D[]
-         {
-           new Static3D( C2/LEN, C1/LEN, 0      ),
-           new Static3D(-C2/LEN, C1/LEN, 0      ),
-           new Static3D( 0     , C2/LEN, C1/LEN ),
-           new Static3D( 0     ,-C2/LEN, C1/LEN ),
-           new Static3D( C1/LEN, 0     , C2/LEN ),
-           new Static3D( C1/LEN, 0     ,-C2/LEN )
-         };
-
-  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 Kilominx
-  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, 8, 12,12,12 },
-           {  6, 5,10, 12,12,12 },
-           {  1, 0,11, 12,12,12 },
-           {  5, 6, 3, 12,12,12 },
-           {  0, 9, 4, 12,12,12 },
-           {  5, 4, 9, 12,12,12 },
-           {  7, 1, 2, 12,12,12 },
-           {  2, 6, 7, 12,12,12 },
-           { 10, 9, 8, 12,12,12 },
-           {  4, 3,11, 12,12,12 },
-           {  7,10, 8, 12,12,12 },
-           {  3, 2,11, 12,12,12 },
-           {  0, 8, 9, 12,12,12 },
-           {  9,10, 5, 12,12,12 },
-           {  0, 4,11, 12,12,12 },
-           {  4, 5, 3, 12,12,12 },
-           {  1, 7, 8, 12,12,12 },
-           {  7, 6,10, 12,12,12 },
-           {  2, 1,11, 12,12,12 },
-           {  6, 2, 3, 12,12,12 },
-         };
-
   private static MeshBase mMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  TwistyKilominx(int size, Static4D quat, DistortedTexture texture,
-                 MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
-    {
-    super(size, size, 30, quat, texture, mesh, effects, moves, ObjectList.KILO, res, scrWidth);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  float getScreenRatio()
-    {
-    return 0.9f;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Static4D[] getQuats()
-    {
-    return QUATS;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getNumFaces()
-    {
-    return FACE_COLORS.length;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean shouldResetTextureMaps()
+  TwistyKilominx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
     {
-    return false;
+    super(size, size, quat, texture, mesh, effects, moves, ObjectList.KILO, res, scrWidth);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -224,79 +60,25 @@ public class TwistyKilominx extends TwistyObject
     return new float[] { -0.5f , 0.5f };
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getNumCubitFaces()
-    {
-    return FACES_PER_CUBIT;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   Static3D[] getCubitPositions(int numLayers)
     {
-    final Static3D[] CENTERS = new Static3D[20];
-
-    CENTERS[ 0] = new Static3D( 0.0f, 0.5f,   C2);
-    CENTERS[ 1] = new Static3D( 0.0f, 0.5f,  -C2);
-    CENTERS[ 2] = new Static3D( 0.0f,-0.5f,   C2);
-    CENTERS[ 3] = new Static3D( 0.0f,-0.5f,  -C2);
-    CENTERS[ 4] = new Static3D(   C2, 0.0f, 0.5f);
-    CENTERS[ 5] = new Static3D(   C2, 0.0f,-0.5f);
-    CENTERS[ 6] = new Static3D(  -C2, 0.0f, 0.5f);
-    CENTERS[ 7] = new Static3D(  -C2, 0.0f,-0.5f);
-    CENTERS[ 8] = new Static3D( 0.5f,   C2, 0.0f);
-    CENTERS[ 9] = new Static3D( 0.5f,  -C2, 0.0f);
-    CENTERS[10] = new Static3D(-0.5f,   C2, 0.0f);
-    CENTERS[11] = new Static3D(-0.5f,  -C2, 0.0f);
-    CENTERS[12] = new Static3D(   C1,   C1,   C1);
-    CENTERS[13] = new Static3D(   C1,   C1,  -C1);
-    CENTERS[14] = new Static3D(   C1,  -C1,   C1);
-    CENTERS[15] = new Static3D(   C1,  -C1,  -C1);
-    CENTERS[16] = new Static3D(  -C1,   C1,   C1);
-    CENTERS[17] = new Static3D(  -C1,   C1,  -C1);
-    CENTERS[18] = new Static3D(  -C1,  -C1,   C1);
-    CENTERS[19] = new Static3D(  -C1,  -C1,  -C1);
-
-    return CENTERS;
+    return CORNERS;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   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 40;
-      case  5: return 31;
-      case  6: return 41;
-      case  7: return 30;
-      case  8: return 39;
-      case  9: return 35;
-      case 10: return 36;
-      case 11: return 34;
-      case 12: return 56;
-      case 13: return 32;
-      case 14: return 43;
-      case 15: return 21;
-      case 16: return 48;
-      case 17: return 28;
-      case 18: return 42;
-      case 19: return 23;
-      }
-
-    return 0;
+    return ( cubit>=0 && cubit<20 ) ? QUAT_INDICES[cubit] : 0;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
-    if( mMesh==null ) mMesh = FactoryCubit.getInstance().createMinxCornerMesh();
+    if( mMesh==null ) mMesh = FactoryCubit.getInstance().createKilominxCornerMesh();
     MeshBase mesh = mMesh.copy(true);
 
     MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[getQuat(cubit)], new Static3D(0,0,0) );
@@ -309,7 +91,7 @@ public class TwistyKilominx extends TwistyObject
 
   int getFaceColor(int cubit, int cubitface, int numLayers)
     {
-    return mFaceMap[cubit][cubitface];
+    return cubitface>=0 && cubitface<3 ? mCornerFaceMap[cubit][cubitface] : NUM_TEXTURES*NUM_FACES;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -335,70 +117,11 @@ public class TwistyKilominx extends TwistyObject
     canvas.drawLine(left+MID-WID,top+HEI,left+MID+WID,top+HEI,paint);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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.
+// All cubits are rotated with the same quat.
 
   public boolean isSolved()
     {
@@ -412,14 +135,6 @@ public class TwistyKilominx extends TwistyObject
     return true;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// only needed for solvers - there are no Kilominx solvers ATM)
-
-  public String retObjectString()
-    {
-    return "";
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getObjectName(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyMegaminx.java b/src/main/java/org/distorted/objects/TwistyMegaminx.java
new file mode 100644
index 00000000..12ea6ad5
--- /dev/null
+++ b/src/main/java/org/distorted/objects/TwistyMegaminx.java
@@ -0,0 +1,458 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 org.distorted.main.RubikSurfaceView;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class TwistyMegaminx extends TwistyMinx
+{
+  static final float MEGA_D = 0.05f;
+
+  private static final int NUM_CORNERS = 20;
+  private static final int NUM_CENTERS = 12;
+  private static final int NUM_EDGES   = 30;
+
+  private static final int[][] mFaceVertexMap =
+         {
+           { 0, 12,  8, 10, 16},
+           { 0, 12,  4, 14,  2},
+           { 2, 14,  9, 11, 18},
+           { 0,  2, 18,  6, 16},
+           { 8, 13,  5,  4, 12},
+           { 4,  5, 15,  9, 14},
+           { 6, 18, 11, 19,  7},
+           {10, 16,  6,  7, 17},
+           { 1, 13,  8, 10, 17},
+           { 1, 13,  5, 15,  3},
+           { 3, 15,  9, 11, 19},
+           { 1,  3, 19,  7, 17},
+         };
+
+  private static MeshBase mCenterMesh, mCornerMesh;
+  private static MeshBase[] mEdgeMesh;
+
+  private static final Static4D[] mBasicV, mCurrV;
+
+  static
+    {
+    float D = 0.5f-MEGA_D;
+
+    mBasicV = new Static4D[3];
+    mCurrV  = new Static4D[3];
+
+    mBasicV[0] = new Static4D( (SQ5+1)*0.125f*D, (SQ5-1)*0.125f*D, 0.250f*D, 0.0f );
+    mBasicV[1] = new Static4D(-(SQ5+1)*0.125f*D, (SQ5-1)*0.125f*D, 0.250f*D, 0.0f );
+    mBasicV[2] = new Static4D(                0,        -0.500f*D,     0.0f, 0.0f );
+    };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TwistyMegaminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
+    {
+    super(size, size, quat, texture, mesh, effects, moves, ObjectList.MEGA, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numCubitsPerCorner(int numLayers)
+    {
+    return 3*((numLayers-1)/2)*((numLayers-3)/2) + 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int numCubitsPerEdge(int numLayers)
+    {
+    return numLayers-2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumStickerTypes(int numLayers)
+    {
+    return 1; //numLayers;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getCuts(int numLayers)
+    {
+    float[] cuts = new float[numLayers-1];
+    float D = (numLayers/3.0f)*MovementMinx.DIST3D;
+    float E = 2*C1;           // 2*cos(36 deg)
+    float X = 2*D*E/(1+2*E);  // height of the 'upper' part of a dodecahedron, i.e. put it on a table,
+                              // its height is then 2*DIST3D, it has one 'lower' part of height X, one
+                              // 'middle' part of height Y and one upper part of height X again.
+                              // It's edge length = numLayers/3.0f.
+    int num = (numLayers-1)/2;
+    float G = X*(0.5f-MEGA_D)/num; // height of one Layer
+
+    for(int i=0; i<num; i++)
+      {
+      cuts[        i] = -MovementMinx.DIST3D + (i+0.5f)*G;
+      cuts[2*num-1-i] = -cuts[i];
+      }
+
+    return cuts;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeCenter(Static3D[] array, int center)
+    {
+    int[] map = mFaceVertexMap[center];
+
+    float x = CORNERS[map[0]].get0() +
+              CORNERS[map[1]].get0() +
+              CORNERS[map[2]].get0() +
+              CORNERS[map[3]].get0() +
+              CORNERS[map[4]].get0() ;
+
+    float y = CORNERS[map[0]].get1() +
+              CORNERS[map[1]].get1() +
+              CORNERS[map[2]].get1() +
+              CORNERS[map[3]].get1() +
+              CORNERS[map[4]].get1() ;
+
+    float z = CORNERS[map[0]].get2() +
+              CORNERS[map[1]].get2() +
+              CORNERS[map[2]].get2() +
+              CORNERS[map[3]].get2() +
+              CORNERS[map[4]].get2() ;
+
+    array[center].set(x/5,y/5,z/5);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Fill out mCurr{X,Y,Z} by applying appropriate Quat to mBasic{X,Y,Z}
+// Appropriate one: QUATS[QUAT_INDICES[corner]].
+
+  private void computeBasicVectors(int corner)
+    {
+    Static4D quat = QUATS[QUAT_INDICES[corner]];
+
+    mCurrV[0] = RubikSurfaceView.rotateVectorByQuat(mBasicV[0],quat);
+    mCurrV[1] = RubikSurfaceView.rotateVectorByQuat(mBasicV[1],quat);
+    mCurrV[2] = RubikSurfaceView.rotateVectorByQuat(mBasicV[2],quat);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeCorner(Static3D pos, int numCubitsPerCorner, int numLayers, int corner, int part)
+    {
+    float D = numLayers/3.0f;
+    Static3D corn = CORNERS[corner];
+
+    if( part==0 )
+      {
+      pos.set( corn.get0()*D, corn.get1()*D, corn.get2()*D );
+      }
+    else
+      {
+      float E = 2.0f/(numLayers-1);
+      int N = (numCubitsPerCorner-1)/3;
+      int b1 = (part-1) % N;
+      int b2 = (part-1) / N;
+      Static4D pri = mCurrV[b2];
+      Static4D sec = mCurrV[(b2+2)%3];
+
+      int multP = b1 % ((numLayers-3)/2);
+      int multS = b1 / ((numLayers-3)/2);
+
+      pos.set( corn.get0()*D + (pri.get0()*multP + sec.get0()*multS)*E,
+               corn.get1()*D + (pri.get1()*multP + sec.get1()*multS)*E,
+               corn.get2()*D + (pri.get2()*multP + sec.get2()*multS)*E );
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  private void computeEdge(Static3D pos, int edge, int part)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getCubitPositions(int numLayers)
+    {
+    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
+  //  int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
+    int numCubits = NUM_CORNERS*numCubitsPerCorner ;//+ NUM_EDGES*numCubitsPerEdge + NUM_CENTERS;
+    int index=0;
+
+    final Static3D[] CENTERS = new Static3D[numCubits];
+
+    for(int corner=0; corner<NUM_CORNERS; corner++)
+      {
+      computeBasicVectors(corner);
+
+      for(int part=0; part<numCubitsPerCorner; part++, index++)
+        {
+        CENTERS[index] = new Static3D(0,0,0);
+        computeCorner(CENTERS[index],numCubitsPerCorner,numLayers,corner,part);
+        }
+      }
+/*
+    for(int edge=0; edge<NUM_EDGES; edge++)
+      {
+      for(int part=0; part<numCubitsPerEdge; part++, index++)
+        {
+        computeEdge(CENTERS,index, edge, part );
+        }
+      }
+
+    for(int center=0; center<NUM_CENTERS; center++, index++)
+      {
+      computeCenter(CENTERS,index);
+      }
+*/
+    return CENTERS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getQuat(int cubit, int numLayers)
+    {
+    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
+
+    if( cubit < NUM_CORNERS*numCubitsPerCorner )
+      {
+      int corner = cubit/numCubitsPerCorner;
+      return QUAT_INDICES[corner];
+      }
+/*
+    if( cubit < NUM_CENTERS + NUM_CORNERS*numCubitsPerCorner )
+      {
+      switch(cubit-NUM_CORNERS*numCubitsPerCorner)
+        {
+        case  0: return  0;
+        case  1: return 52;
+        case  2: return  3;
+        case  3: return 53;
+        case  4: return 59;
+        case  5: return 20;
+        case  6: return 22;
+        case  7: return 58;
+        case  8: return 55;
+        case  9: return 13;
+        case 10: return  1;
+        case 11: return 14;
+        }
+      }
+
+    int numCubitsPerEdge = numCubitsPerEdge(numLayers);
+
+    // TODO: edges
+*/
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCubitMesh(int cubit, int numLayers)
+    {
+    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
+    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
+    MeshBase mesh;
+
+//    if( cubit < NUM_CORNERS*numCubitsPerCorner )
+      {
+      if( mCenterMesh==null ) mCenterMesh = FactoryCubit.getInstance().createMegaminxCornerMesh(numLayers);
+      mesh = mCenterMesh.copy(true);
+      }
+/*
+    else if( cubit<NUM_CENTERS + NUM_CORNERS*numCubitsPerCorner )
+      {
+      if( mCornerMesh==null ) mCornerMesh = FactoryCubit.getInstance().createMegaminxCenterMesh();
+      mesh = mCornerMesh.copy(true);
+      }
+    else
+      {
+      if( mEdgeMesh==null ) mEdgeMesh = new MeshBase[(numLayers-1)/2];
+
+      // TODO
+      }
+*/
+    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[getQuat(cubit,numLayers)], new Static3D(0,0,0) );
+    mesh.apply(quat,0xffffffff,0);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  int getFaceColor(int cubit, int cubitface, int numLayers)
+    {
+    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
+    int type  = cubit % numCubitsPerCorner;
+    int corner= cubit / numCubitsPerCorner;
+
+    if( type==0 )
+      {
+      return cubitface>=0 && cubitface<3 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
+      }
+    else
+      {
+      int N = (numCubitsPerCorner-1)/3;
+      int block = (type-1) % N;
+      int index = (type-1) / N;
+
+      if( index< (numLayers-1)/2 )
+        {
+        return ( cubitface>2 || cubitface==((block+2)%3) ) ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
+        }
+      else
+        {
+        return ( cubitface>2 || cubitface==block         ) ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
+    {
+    paint.setColor(FACE_COLORS[face]);
+    paint.setStyle(Paint.Style.FILL);
+    canvas.drawRect(left,top,left+TEXTURE_HEIGHT,top+TEXTURE_HEIGHT,paint);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public boolean isSolved()
+    {
+    int index = CUBITS[0].mQuatIndex;
+
+    for(int i=1; i<NUM_CUBITS; i++)
+      {
+      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
+      }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
+// then if it were rotated by quaternion 'quat'.
+// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
+// middle squares get interchanged. No visible difference!
+//
+// So: this is true iff the cubit
+// a) is a corner or edge and the quaternions are the same
+// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
+
+  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
+    {
+    if ( cubit.mQuatIndex == quatIndex ) return true;
+
+    int belongsToHowManyFaces = 0;
+    int lastLayer = getNumLayers()-1;
+    float row;
+    final float MAX_ERROR = 0.01f;
+
+    for(int i=0; i<NUM_AXIS; i++)
+      {
+      row = cubit.mRotationRow[i];
+      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
+          (row-lastLayer<MAX_ERROR && row-lastLayer>-MAX_ERROR)  ) belongsToHowManyFaces++;
+      }
+
+    switch(belongsToHowManyFaces)
+      {
+      case 0 : return true ;  // 'inside' cubit that does not lie on any face
+      case 1 :                // cubit that lies inside one of the faces
+               Static3D orig = cubit.getOrigPosition();
+               Static4D quat1 = QUATS[quatIndex];
+               Static4D quat2 = QUATS[cubit.mQuatIndex];
+
+               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
+               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
+               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
+
+               float row1, row2;
+               float x1 = rotated1.get0();
+               float y1 = rotated1.get1();
+               float z1 = rotated1.get2();
+               float x2 = rotated2.get0();
+               float y2 = rotated2.get1();
+               float z2 = rotated2.get2();
+
+               for(int i=0; i<NUM_AXIS; i++)
+                 {
+                 row1 = computeRow(x1,y1,z1,i);
+                 row2 = computeRow(x2,y2,z2,i);
+
+                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return true;
+                 }
+               return false;
+
+      default: return false;  // edge or corner
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getObjectName(int numLayers)
+    {
+    if( numLayers==3 ) return R.string.minx3;
+    if( numLayers==5 ) return R.string.minx4;
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getInventor(int numLayers)
+    {
+    if( numLayers==3 ) return R.string.minx3_inventor;
+    if( numLayers==5 ) return R.string.minx4_inventor;
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getComplexity(int numLayers)
+    {
+    if( numLayers==3 ) return 4;
+
+    return 5;
+    }
+}
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..1c0ed99a
--- /dev/null
+++ b/src/main/java/org/distorted/objects/TwistyMinx.java
@@ -0,0 +1,314 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshSquare;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+abstract class TwistyMinx extends TwistyObject
+{
+  private static final int FACES_PER_CUBIT =6;
+
+  static final float C0 = (SQ5-1)/4;                       // cos(72 deg)
+  static final float C1 = (SQ5+1)/4;                       // cos(36 deg)
+  static final float C2 = (SQ5+3)/4;
+  static final float LEN= (float)(Math.sqrt(1.25f+0.5f*SQ5));
+
+  // the six rotation axis of a Minx. Must be normalized.
+  static final Static3D[] ROT_AXIS = new Static3D[]
+         {
+           new Static3D( C2/LEN, C1/LEN, 0      ),
+           new Static3D(-C2/LEN, C1/LEN, 0      ),
+           new Static3D( 0     , C2/LEN, C1/LEN ),
+           new Static3D( 0     ,-C2/LEN, C1/LEN ),
+           new Static3D( C1/LEN, 0     , C2/LEN ),
+           new Static3D( C1/LEN, 0     ,-C2/LEN )
+         };
+
+  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;
+
+  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 Minx
+  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 ),
+         };
+
+  // Coordinates of all 20 corners of a Minx
+  static final Static3D[] CORNERS = new Static3D[]
+         {
+           new Static3D( 0.0f, 0.5f,   C2),
+           new Static3D( 0.0f, 0.5f,  -C2),
+           new Static3D( 0.0f,-0.5f,   C2),
+           new Static3D( 0.0f,-0.5f,  -C2),
+           new Static3D(   C2, 0.0f, 0.5f),
+           new Static3D(   C2, 0.0f,-0.5f),
+           new Static3D(  -C2, 0.0f, 0.5f),
+           new Static3D(  -C2, 0.0f,-0.5f),
+           new Static3D( 0.5f,   C2, 0.0f),
+           new Static3D( 0.5f,  -C2, 0.0f),
+           new Static3D(-0.5f,   C2, 0.0f),
+           new Static3D(-0.5f,  -C2, 0.0f),
+           new Static3D(   C1,   C1,   C1),
+           new Static3D(   C1,   C1,  -C1),
+           new Static3D(   C1,  -C1,   C1),
+           new Static3D(   C1,  -C1,  -C1),
+           new Static3D(  -C1,   C1,   C1),
+           new Static3D(  -C1,   C1,  -C1),
+           new Static3D(  -C1,  -C1,   C1),
+           new Static3D(  -C1,  -C1,  -C1),
+         };
+
+  static final int[][] mCornerFaceMap =
+         {
+           {  0, 1, 8 },
+           {  6, 5,10 },
+           {  1, 0,11 },
+           {  5, 6, 3 },
+           {  0, 9, 4 },
+           {  5, 4, 9 },
+           {  7, 1, 2 },
+           {  2, 6, 7 },
+           { 10, 9, 8 },
+           {  4, 3,11 },
+           {  7,10, 8 },
+           {  3, 2,11 },
+           {  0, 8, 9 },
+           {  9,10, 5 },
+           {  0, 4,11 },
+           {  4, 5, 3 },
+           {  1, 7, 8 },
+           {  7, 6,10 },
+           {  2, 1,11 },
+           {  6, 2, 3 },
+         };
+
+  static final int[] QUAT_INDICES = {0,2,3,1,40,31,41,30,39,35,36,34,56,32,43,21,48,28,42,23};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TwistyMinx(int numLayers, int realSize, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+             DistortedEffects effects, int[][] moves, ObjectList obj, Resources res, int scrWidth)
+    {
+    super(numLayers, realSize, 30, quat, texture, mesh, effects, moves, obj, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float getScreenRatio()
+    {
+    return 0.9f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static4D[] getQuats()
+    {
+    return QUATS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumFaces()
+    {
+    return FACE_COLORS.length;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean shouldResetTextureMaps()
+    {
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitFaces()
+    {
+    return FACES_PER_CUBIT;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float returnMultiplier()
+    {
+    return 2.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances(int numLayers)
+    {
+    float[] chances = new float[numLayers];
+    float denom = (float)(numLayers-1);
+    int change  = (numLayers-1)/2;
+
+    for(int i=     0; i<change   ; i++) chances[i] = (i+1)/denom;
+    for(int i=change; i<numLayers; i++) chances[i] = (i  )/denom;
+
+    return chances;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// only needed for solvers - there are no Minx solvers ATM)
+
+  public String retObjectString()
+    {
+    return "";
+    }
+}
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index b284c434..e2446c68 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -79,7 +79,7 @@ public abstract class TwistyObject extends DistortedNode
   private static final float MAX_SIZE_CHANGE = 1.35f;
   private static final float MIN_SIZE_CHANGE = 0.75f;
 
-  private static final boolean mCreateFromDMesh = true;
+  private static final boolean mCreateFromDMesh = false;
 
   private static final Static3D CENTER = new Static3D(0,0,0);
   private static final int POST_ROTATION_MILLISEC = 500;
@@ -144,7 +144,7 @@ public abstract class TwistyObject extends DistortedNode
     mInitScreenRatio = getScreenRatio();
     NUM_FACES = getNumFaces();
     NUM_CUBIT_FACES = getNumCubitFaces();
-    NUM_TEXTURES = getNumStickerTypes()*NUM_FACES;
+    NUM_TEXTURES = getNumStickerTypes(mNumLayers)*NUM_FACES;
     CUTS = getCuts(mNumLayers);
     NUM_CUTS = CUTS.length;
 
@@ -156,7 +156,7 @@ public abstract class TwistyObject extends DistortedNode
     mNodeScale= new Static3D(1,NODE_RATIO,1);
     mQuat = quat;
 
-    mRowChances = getRowChances();
+    mRowChances = getRowChances(mNumLayers);
 
     mRotationAngle= new Dynamic1D();
     mRotationAxis = new Static3D(1,0,0);
@@ -245,7 +245,7 @@ public abstract class TwistyObject extends DistortedNode
       for(int i=0; i<NUM_CUBITS; i++)
         {
         CUBITS[i] = new Cubit(this,mOrigPos[i]);
-        cubitMesh[i] = createCubitMesh(i);
+        cubitMesh[i] = createCubitMesh(i,mNumLayers);
         cubitMesh[i].apply(new MatrixEffectMove(mOrigPos[i]),1,0);
         cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
         }
@@ -763,13 +763,13 @@ public abstract class TwistyObject extends DistortedNode
   abstract Static3D[] getCubitPositions(int numLayers);
   abstract Static4D[] getQuats();
   abstract int getNumFaces();
-  abstract int getNumStickerTypes();
+  abstract int getNumStickerTypes(int numLayers);
   abstract int getNumCubitFaces();
-  abstract MeshBase createCubitMesh(int cubit);
+  abstract MeshBase createCubitMesh(int cubit, int numLayers);
   abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top);
   abstract int getFaceColor(int cubit, int cubitface, int numLayers);
   abstract float returnMultiplier();
-  abstract float[] getRowChances();
+  abstract float[] getRowChances(int numLayers);
   abstract float[] getCuts(int numLayers);
   abstract boolean shouldResetTextureMaps();
 
diff --git a/src/main/java/org/distorted/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objects/TwistyPyraminx.java
index eb1b62c2..fb95a70f 100644
--- a/src/main/java/org/distorted/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objects/TwistyPyraminx.java
@@ -149,7 +149,7 @@ public class TwistyPyraminx extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 1;
     }
@@ -220,10 +220,8 @@ public class TwistyPyraminx extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
-    int numLayers = getNumLayers();
-
     if( cubit< (numLayers-1)*numLayers*(numLayers+1)/6 )
       {
       if( mOctaMesh==null ) mOctaMesh = FactoryCubit.getInstance().createOctaMesh();
@@ -260,9 +258,8 @@ public class TwistyPyraminx extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
-    int numLayers = getNumLayers();
     int total = numLayers*(numLayers+1)/2;
     float running=0.0f;
     float[] chances = new float[numLayers];
diff --git a/src/main/java/org/distorted/objects/TwistyRedi.java b/src/main/java/org/distorted/objects/TwistyRedi.java
index 216ba1c4..adf45e58 100644
--- a/src/main/java/org/distorted/objects/TwistyRedi.java
+++ b/src/main/java/org/distorted/objects/TwistyRedi.java
@@ -31,7 +31,6 @@ import org.distorted.library.mesh.MeshSquare;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.main.R;
-import org.distorted.main.RubikSurfaceView;
 
 import java.util.Random;
 
@@ -174,7 +173,7 @@ public class TwistyRedi extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 2;
     }
@@ -239,7 +238,7 @@ public class TwistyRedi extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
@@ -303,7 +302,7 @@ public class TwistyRedi extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     float[] chances = new float[3];
 
@@ -353,47 +352,6 @@ public class TwistyRedi extends TwistyObject
     return (oldRotAxis==START_AXIS) ? (rnd.nextFloat()<=0.5f ? 0:2) : (oldRotAxis+newRotAxis==3 ? 2-oldRow : oldRow);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// remember about the double cover or unit quaternions!
-
-  private int mulQuat(int q1, int q2)
-    {
-    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
-
-    float rX = result.get0();
-    float rY = result.get1();
-    float rZ = result.get2();
-    float rW = result.get3();
-
-    final float MAX_ERROR = 0.1f;
-    float dX,dY,dZ,dW;
-
-    for(int i=0; i<QUATS.length; i++)
-      {
-      dX = QUATS[i].get0() - rX;
-      dY = QUATS[i].get1() - rY;
-      dZ = QUATS[i].get2() - rZ;
-      dW = QUATS[i].get3() - rW;
-
-      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
-          dY<MAX_ERROR && dY>-MAX_ERROR &&
-          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
-          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
-
-      dX = QUATS[i].get0() + rX;
-      dY = QUATS[i].get1() + rY;
-      dZ = QUATS[i].get2() + rZ;
-      dW = QUATS[i].get3() + rW;
-
-      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
-          dY<MAX_ERROR && dY>-MAX_ERROR &&
-          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
-          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
-      }
-
-    return -1;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // The Redi is solved if and only if:
 //
diff --git a/src/main/java/org/distorted/objects/TwistyRex.java b/src/main/java/org/distorted/objects/TwistyRex.java
index b2a7ecea..3f2c8392 100644
--- a/src/main/java/org/distorted/objects/TwistyRex.java
+++ b/src/main/java/org/distorted/objects/TwistyRex.java
@@ -166,7 +166,7 @@ public class TwistyRex extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 3;
     }
@@ -301,7 +301,7 @@ public class TwistyRex extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
@@ -371,7 +371,7 @@ public class TwistyRex extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
     return new float[] { 0.5f, 0.5f, 1.0f };
     }
diff --git a/src/main/java/org/distorted/objects/TwistySkewb.java b/src/main/java/org/distorted/objects/TwistySkewb.java
index 2ebe5e8f..90bd6952 100644
--- a/src/main/java/org/distorted/objects/TwistySkewb.java
+++ b/src/main/java/org/distorted/objects/TwistySkewb.java
@@ -178,7 +178,7 @@ public class TwistySkewb extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int getNumStickerTypes()
+  int getNumStickerTypes(int numLayers)
     {
     return 3;
     }
@@ -400,11 +400,10 @@ public class TwistySkewb extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createCubitMesh(int cubit)
+  MeshBase createCubitMesh(int cubit, int numLayers)
     {
     MeshBase mesh;
 
-    int numLayers  = getNumLayers();
     int numCorners = getNumCorners();
     int numEdges   = getNumEdges(numLayers);
 
@@ -503,9 +502,8 @@ public class TwistySkewb extends TwistyObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[] getRowChances()
+  float[] getRowChances(int numLayers)
     {
-    int numLayers = getNumLayers();
     float[] chances = new float[numLayers];
 
     switch(numLayers)
