commit b1f0d55d0833ec3fb595c99d7c29551180568cb4
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Apr 25 23:53:12 2022 +0200

    Bandaged: generalize the FactoryBandagedCubit to be able to create bandaged cubits of any cuboid NxMxK where 1<=N,M,K<=5.

diff --git a/src/main/java/org/distorted/objectlib/helpers/FactoryBandaged3x3Cubit.java b/src/main/java/org/distorted/objectlib/helpers/FactoryBandaged3x3Cubit.java
deleted file mode 100644
index 55faf603..00000000
--- a/src/main/java/org/distorted/objectlib/helpers/FactoryBandaged3x3Cubit.java
+++ /dev/null
@@ -1,1199 +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.objectlib.helpers;
-
-import java.util.ArrayList;
-
-import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.type.Static3D;
-import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
-
-import static org.distorted.objectlib.main.TwistyObject.MESH_NICE;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class FactoryBandaged3x3Cubit
-  {
-  private static final int WALL_MARKED=0;
-  private static final int WALL_EMPTY =-1;
-
-  private static final int AXIS_XP = 0;
-  private static final int AXIS_XM = 1;
-  private static final int AXIS_YP = 2;
-  private static final int AXIS_YM = 3;
-  private static final int AXIS_ZP = 4;
-  private static final int AXIS_ZM = 5;
-
-  private static final float[][] VECTOR =
-      {
-          {-1.0f,-1.0f,-1.0f},
-          {-1.0f,-1.0f,+1.0f},
-          {-1.0f,+1.0f,-1.0f},
-          {-1.0f,+1.0f,+1.0f},
-          {+1.0f,-1.0f,-1.0f},
-          {+1.0f,-1.0f,+1.0f},
-          {+1.0f,+1.0f,-1.0f},
-          {+1.0f,+1.0f,+1.0f}
-      };
-
-  private static FactoryBandaged3x3Cubit mThis;
-  private ArrayList<float[]> mVertexArray;
-  private ArrayList<float[]> mTmpArray;
-  private float[][][] mVertices;
-  private int[][][] mIndices;
-  private float[][] mMove;
-  private final int[] mWall;
-  private final int[][] mPoints;
-  private final boolean[][][] mTmp;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private FactoryBandaged3x3Cubit()
-    {
-    mWall= new int[9];
-    mPoints = new int[4][4];
-    mTmp = new boolean[3][3][3];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[][] getVertices(ArrayList<float[]> list, float[] move, int variant)
-    {
-    int numMoves = move.length/3;
-    mMove[variant][0]=0.0f;
-    mMove[variant][1]=0.0f;
-    mMove[variant][2]=0.0f;
-
-    for(int m=0; m<numMoves; m++)
-      {
-      mMove[variant][0] += move[3*m  ];
-      mMove[variant][1] += move[3*m+1];
-      mMove[variant][2] += move[3*m+2];
-      }
-
-    mMove[variant][0]/=numMoves;
-    mMove[variant][1]/=numMoves;
-    mMove[variant][2]/=numMoves;
-
-    int total  = 0;
-    int length = list.size();
-    float[][] vertices = new float[length][];
-
-    for( int i=0; i<length; i++ )
-      {
-      vertices[i] = list.get(i);
-      total += vertices[i].length/3;
-      }
-
-    float[][] verts = new float[total][3];
-    int pointer = 0;
-
-    for(int i=0; i<length; i++)
-      {
-      int len = vertices[i].length/3;
-
-      for(int j=0; j<len; j++)
-        {
-        verts[pointer][0] = vertices[i][3*j  ] - mMove[variant][0];
-        verts[pointer][1] = vertices[i][3*j+1] - mMove[variant][1];
-        verts[pointer][2] = vertices[i][3*j+2] - mMove[variant][2];
-        pointer++;
-        }
-      }
-
-    return verts;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int[][] getIndices(ArrayList<float[]> list)
-    {
-    int indicesSoFar=0;
-    int length = list.size();
-    int[][] indices = new int[length][];
-
-    for( int i=0; i<length; i++ )
-      {
-      float[] f = list.get(i);
-      int len = f.length/3;
-      int[] ind = new int[len];
-      for(int j=0; j<len; j++) ind[j] = (indicesSoFar++);
-      indices[i] = ind;
-      }
-
-    return indices;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void markAllVertices(float[] vertex, float[][] vertices, int[][] indices, int pointer, int variant)
-    {
-    int lenI = indices.length;
-
-    for(int index=0; index<lenI; index++)
-      {
-      int len = indices[index].length;
-
-      for(int i=0; i<len; i++)
-        {
-        if( mIndices[variant][index][i] == -1 )
-          {
-          int ind = indices[index][i];
-          float[] ver = vertices[ind];
-
-          if( vertex[0]==ver[0] && vertex[1]==ver[1] && vertex[2]==ver[2] )
-            {
-            mIndices[variant][index][i] = pointer;
-            }
-          }
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// So far the 'vertices/indices' are stored inefficiently, with each vertex stored three times
-// (each one normally is a corner of three faces) or even six times. Compress!
-// Example of six times: the central vertex here:
-//
-// { 1.0f,  0.0f, -1.0f,
-//   1.0f, -1.0f, -1.0f,
-//   1.0f, -1.0f, +0.0f,
-//   0.0f, -1.0f, -1.0f },
-
-  private void compressVerticesAndIndices(int variant, float[][] vertices, int[][] indices)
-    {
-    if( mTmpArray==null ) mTmpArray = new ArrayList<>();
-
-    int lenI = indices.length;
-    int pointer=0;
-
-    mIndices[variant] = new int[lenI][];
-
-    for(int index=0; index<lenI; index++)
-      {
-      int len = indices[index].length;
-      mIndices[variant][index] = new int[len];
-      for(int i=0; i<len; i++) mIndices[variant][index][i] = -1;
-      }
-
-    for(int index=0; index<lenI; index++)
-      {
-      int len = indices[index].length;
-
-      for(int i=0; i<len; i++)
-        {
-        if( mIndices[variant][index][i] == -1 )
-          {
-          int ind = indices[index][i];
-          float[] ver = vertices[ind];
-          mTmpArray.add(ver);
-          markAllVertices(ver,vertices,indices,pointer,variant);
-          pointer++;
-          }
-        }
-      }
-
-    int len = mTmpArray.size();
-    mVertices[variant] = new float[len][];
-
-    for(int i=0; i<len; i++)
-      {
-      mVertices[variant][i] = mTmpArray.remove(0);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean cubitExists(float[] pos, float x, float y, float z)
-    {
-    int len = pos.length/3;
-
-    for(int i=0; i<len; i++)
-      if( pos[3*i]==x && pos[3*i+1]==y && pos[3*i+2]==z ) return true;
-
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createRight0(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][2][2] && !mTmp[1][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[0][2][1] && !mTmp[1][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[0][2][0] && !mTmp[1][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][1][2] && !mTmp[1][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[0][1][1] && !mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[0][1][0] && !mTmp[1][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][0][2] && !mTmp[1][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[0][0][1] && !mTmp[1][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[0][0][0] && !mTmp[1][0][0]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XP,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createRight1(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[1][2][2] && !mTmp[2][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][1] && !mTmp[2][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[1][2][0] && !mTmp[2][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[1][1][2] && !mTmp[2][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][1] && !mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[1][1][0] && !mTmp[2][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[1][0][2] && !mTmp[2][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][1] && !mTmp[2][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[1][0][0] && !mTmp[2][0][0]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XP,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createRight2(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[2][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[2][2][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[2][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[2][1][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[2][1][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[2][1][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[2][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[2][0][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[2][0][0] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XP,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createLeft0(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[0][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[0][2][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[0][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[0][1][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[0][1][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[0][1][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[0][0][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[0][0][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[0][0][2] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XM,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createLeft1(ArrayList<float[]> list)
-    {
-    mWall[0] = (!mTmp[0][2][0] && mTmp[1][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (!mTmp[0][2][1] && mTmp[1][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (!mTmp[0][2][2] && mTmp[1][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (!mTmp[0][1][0] && mTmp[1][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (!mTmp[0][1][1] && mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (!mTmp[0][1][2] && mTmp[1][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (!mTmp[0][0][0] && mTmp[1][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (!mTmp[0][0][1] && mTmp[1][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (!mTmp[0][0][2] && mTmp[1][0][2]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XM,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createLeft2(ArrayList<float[]> list)
-    {
-    mWall[0] = (!mTmp[1][2][0] && mTmp[2][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (!mTmp[1][2][1] && mTmp[2][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (!mTmp[1][2][2] && mTmp[2][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (!mTmp[1][1][0] && mTmp[2][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (!mTmp[1][1][1] && mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (!mTmp[1][1][2] && mTmp[2][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (!mTmp[1][0][0] && mTmp[2][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (!mTmp[1][0][1] && mTmp[2][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (!mTmp[1][0][2] && mTmp[2][0][2]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_XM,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createTop0(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][0][0] && !mTmp[0][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][0][0] && !mTmp[1][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][0][0] && !mTmp[2][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][0][1] && !mTmp[0][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][0][1] && !mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][0][1] && !mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][0][2] && !mTmp[0][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][2] && !mTmp[1][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][0][2] && !mTmp[2][1][2]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YP,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createTop1(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][1][0] && !mTmp[0][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][1][0] && !mTmp[1][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][1][0] && !mTmp[2][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][1][1] && !mTmp[0][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][1] && !mTmp[1][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][1][1] && !mTmp[2][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][1][2] && !mTmp[0][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][1][2] && !mTmp[1][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][1][2] && !mTmp[2][2][2]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YP,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createTop2(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[0][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[1][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[2][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[0][2][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[1][2][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[2][2][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[0][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[1][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[2][2][2] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YP,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBottom0(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[0][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[1][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[2][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[0][0][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[1][0][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[2][0][1] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[0][0][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[1][0][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[2][0][0] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YM,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBottom1(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][1][2] && !mTmp[0][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][1][2] && !mTmp[1][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][1][2] && !mTmp[2][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][1][1] && !mTmp[0][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][1] && !mTmp[1][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][1][1] && !mTmp[2][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][1][0] && !mTmp[0][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][1][0] && !mTmp[1][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][1][0] && !mTmp[2][0][0]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YM,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBottom2(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][2][2] && !mTmp[0][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][2] && !mTmp[1][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][2][2] && !mTmp[2][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][2][1] && !mTmp[0][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][2][1] && !mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][2][1] && !mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][2][0] && !mTmp[0][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][2][0] && !mTmp[1][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][2][0] && !mTmp[2][1][0]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_YM,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createFront0(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][2][0] && !mTmp[0][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][0] && !mTmp[1][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][2][0] && !mTmp[2][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][1][0] && !mTmp[0][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][0] && !mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][1][0] && !mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][0][0] && !mTmp[0][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][0] && !mTmp[1][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][0][0] && !mTmp[2][0][1]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZP,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createFront1(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[0][2][1] && !mTmp[0][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][1] && !mTmp[1][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[2][2][1] && !mTmp[2][2][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[0][1][1] && !mTmp[0][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][1] && !mTmp[1][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[2][1][1] && !mTmp[2][1][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[0][0][1] && !mTmp[0][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][1] && !mTmp[1][0][2]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[2][0][1] && !mTmp[2][0][2]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZP,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createFront2(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[0][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[1][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[2][2][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[0][1][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[1][1][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[2][1][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[0][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[1][0][2] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[2][0][2] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZP,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBack0(ArrayList<float[]> list)
-    {
-    mWall[0] = mTmp[2][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = mTmp[1][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = mTmp[0][2][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = mTmp[2][1][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = mTmp[1][1][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = mTmp[0][1][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = mTmp[2][0][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = mTmp[1][0][0] ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = mTmp[0][0][0] ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZM,0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBack1(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[2][2][1] && !mTmp[2][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][1] && !mTmp[1][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[0][2][1] && !mTmp[0][2][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[2][1][1] && !mTmp[2][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][1] && !mTmp[1][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[0][1][1] && !mTmp[0][1][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[2][0][1] && !mTmp[2][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][1] && !mTmp[1][0][0]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[0][0][1] && !mTmp[0][0][0]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZM,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBack2(ArrayList<float[]> list)
-    {
-    mWall[0] = (mTmp[2][2][2] && !mTmp[2][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[1] = (mTmp[1][2][2] && !mTmp[1][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[2] = (mTmp[0][2][2] && !mTmp[0][2][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[3] = (mTmp[2][1][2] && !mTmp[2][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[4] = (mTmp[1][1][2] && !mTmp[1][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[5] = (mTmp[0][1][2] && !mTmp[0][1][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[6] = (mTmp[2][0][2] && !mTmp[2][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[7] = (mTmp[1][0][2] && !mTmp[1][0][1]) ? WALL_MARKED:WALL_EMPTY;
-    mWall[8] = (mTmp[0][0][2] && !mTmp[0][0][1]) ? WALL_MARKED:WALL_EMPTY;
-
-    createVertices(list,mWall,AXIS_ZM,2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void markNeighbours(int[] wall, int index, int section)
-    {
-    wall[index] = section;
-
-    switch(index)
-      {
-      case 0: if( wall[1]==WALL_MARKED ) markNeighbours(wall,1,section);
-              if( wall[3]==WALL_MARKED ) markNeighbours(wall,3,section);
-              break;
-      case 1: if( wall[0]==WALL_MARKED ) markNeighbours(wall,0,section);
-              if( wall[2]==WALL_MARKED ) markNeighbours(wall,2,section);
-              if( wall[4]==WALL_MARKED ) markNeighbours(wall,4,section);
-              break;
-      case 2: if( wall[1]==WALL_MARKED ) markNeighbours(wall,1,section);
-              if( wall[5]==WALL_MARKED ) markNeighbours(wall,5,section);
-              break;
-      case 3: if( wall[0]==WALL_MARKED ) markNeighbours(wall,0,section);
-              if( wall[4]==WALL_MARKED ) markNeighbours(wall,4,section);
-              if( wall[6]==WALL_MARKED ) markNeighbours(wall,6,section);
-              break;
-      case 4: if( wall[1]==WALL_MARKED ) markNeighbours(wall,1,section);
-              if( wall[3]==WALL_MARKED ) markNeighbours(wall,3,section);
-              if( wall[5]==WALL_MARKED ) markNeighbours(wall,5,section);
-              if( wall[7]==WALL_MARKED ) markNeighbours(wall,7,section);
-              break;
-      case 5: if( wall[2]==WALL_MARKED ) markNeighbours(wall,2,section);
-              if( wall[4]==WALL_MARKED ) markNeighbours(wall,4,section);
-              if( wall[8]==WALL_MARKED ) markNeighbours(wall,8,section);
-              break;
-      case 6: if( wall[3]==WALL_MARKED ) markNeighbours(wall,3,section);
-              if( wall[7]==WALL_MARKED ) markNeighbours(wall,7,section);
-              break;
-      case 7: if( wall[4]==WALL_MARKED ) markNeighbours(wall,4,section);
-              if( wall[6]==WALL_MARKED ) markNeighbours(wall,6,section);
-              if( wall[8]==WALL_MARKED ) markNeighbours(wall,8,section);
-              break;
-      case 8: if( wall[5]==WALL_MARKED ) markNeighbours(wall,5,section);
-              if( wall[7]==WALL_MARKED ) markNeighbours(wall,7,section);
-              break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int markSections(int[] wall)
-    {
-    int sections = 0;
-
-    for(int i=0; i<9; i++)
-      if( wall[i]==WALL_MARKED )
-        {
-        sections++;
-        markNeighbours(wall,i,sections);
-        }
-
-    return sections;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return true iff exactly three or exactly one of the four values (x1,x2,x3,x4) are equal to 'value'.
-
-  private boolean threeOrOne(int x1, int x2, int x3, int x4, int value)
-    {
-    if( x1==value ) return x2==value ? (x3==value)^(x4==value) : (x3==value)^(x4!=value);
-    else            return x2==value ? (x3==value)^(x4!=value) : (x3==value)^(x4==value);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int buildPoints(int[] wall, int section)
-    {
-    mPoints[0][0] = (wall[0]==section                   ) ? 0 : -1;
-    mPoints[1][0] = (wall[0]==section)^(wall[1]==section) ? 0 : -1;
-    mPoints[2][0] = (wall[1]==section)^(wall[2]==section) ? 0 : -1;
-    mPoints[3][0] = (wall[2]==section                   ) ? 0 : -1;
-    mPoints[0][1] = (wall[0]==section)^(wall[3]==section) ? 0 : -1;
-    mPoints[1][1] = threeOrOne(wall[0],wall[1],wall[3],wall[4],section) ? 0 : -1;
-    mPoints[2][1] = threeOrOne(wall[1],wall[2],wall[4],wall[5],section) ? 0 : -1;
-    mPoints[3][1] = (wall[2]==section)^(wall[5]==section) ? 0 : -1;
-    mPoints[0][2] = (wall[3]==section)^(wall[6]==section) ? 0 : -1;
-    mPoints[1][2] = threeOrOne(wall[3],wall[4],wall[6],wall[7],section) ? 0 : -1;
-    mPoints[2][2] = threeOrOne(wall[4],wall[5],wall[7],wall[8],section) ? 0 : -1;
-    mPoints[3][2] = (wall[5]==section)^(wall[8]==section) ? 0 : -1;
-    mPoints[0][3] = (wall[6]==section                   ) ? 0 : -1;
-    mPoints[1][3] = (wall[6]==section)^(wall[7]==section) ? 0 : -1;
-    mPoints[2][3] = (wall[7]==section)^(wall[8]==section) ? 0 : -1;
-    mPoints[3][3] = (wall[8]==section                   ) ? 0 : -1;
-
-    int numPoints=0;
-
-    for(int i=0; i<4; i++)
-      for(int j=0; j<4; j++)
-        {
-        if( mPoints[i][j]==0 ) numPoints++;
-        }
-
-    return numPoints;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean isOddVertical(int x, int y)
-    {
-    int number = 0;
-
-    for(int i=0; i<y; i++)
-      if( mPoints[x][i]==0 ) number++;
-
-    return (number%2)==0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private boolean isOddHorizontal(int x, int y)
-    {
-    int number = 0;
-
-    for(int i=0; i<x; i++)
-      if( mPoints[i][y]==0 ) number++;
-
-    return (number%2)==0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int moveUp(int x, int y)
-    {
-    for(int i=y-1; i>=0; i--)
-      if( mPoints[x][i]==0 ) return i;
-
-    android.util.Log.e("D", "moveUp error!");
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int moveDown(int x, int y)
-    {
-    for(int i=y+1; i<4; i++)
-      if( mPoints[x][i]==0 ) return i;
-
-    android.util.Log.e("D", "moveDown error!");
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int moveLeft(int x, int y)
-    {
-    for(int i=x-1; i>=0; i--)
-      if( mPoints[i][y]==0 ) return i;
-
-    android.util.Log.e("D", "moveLeft error!");
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int moveRight(int x, int y)
-    {
-    for(int i=x+1; i<4; i++)
-      if( mPoints[i][y]==0 ) return i;
-
-    android.util.Log.e("D", "moveRight error!");
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[] buildVertices(int[] wall, int section)
-    {
-    int numPoints = buildPoints(wall,section);
-    int numSections = numPoints/2;
-    float[] vertices = new float[3*numPoints];
-    int x=0,y=0,pointer=0;
-
-    for(int i=0; i<4; i++)
-      for(int j=0; j<4; j++)
-        if( mPoints[i][j]==0 )
-          {
-          x = i;
-          y = j;
-          i = j = 4;
-          }
-
-    for(int s=0; s<numSections; s++)
-      {
-      vertices[6*pointer  ] = x-1.5f;
-      vertices[6*pointer+1] = 1.5f-y;
-      vertices[6*pointer+2] = 0.0f;
-
-      y = isOddVertical(x,y) ? moveDown(x,y) : moveUp(x,y);
-
-      vertices[6*pointer+3] = x-1.5f;
-      vertices[6*pointer+4] = 1.5f-y;
-      vertices[6*pointer+5] = 0.0f;
-
-      x = isOddHorizontal(x,y) ? moveRight(x,y) : moveLeft(x,y);
-
-      pointer++;
-      }
-
-    return vertices;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void rotateAndMoveVertices(float[] vertices, int axis, int layer)
-    {
-    int i,len = vertices.length/3;
-
-    switch(axis)
-      {
-      case AXIS_XP: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i+2] = -vertices[3*i];
-                      vertices[3*i  ] = layer-0.5f;
-                      }
-                    break;
-      case AXIS_XM: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i+2] = vertices[3*i];
-                      vertices[3*i  ] = layer-1.5f;
-                      }
-                    break;
-      case AXIS_YP: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i+2] = -vertices[3*i+1];
-                      vertices[3*i+1] = layer-0.5f;
-                      }
-                    break;
-      case AXIS_YM: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i+2] = vertices[3*i+1];
-                      vertices[3*i+1] = layer-1.5f;
-                      }
-                    break;
-      case AXIS_ZP: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i+2] = layer-0.5f;
-                      }
-                    break;
-      case AXIS_ZM: for(i=0; i<len; i++)
-                      {
-                      vertices[3*i  ] = -vertices[3*i];
-                      vertices[3*i+2] = layer-1.5f;
-                      }
-                    break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 1. assume the 'wall' is in the XY plane
-// 2. split the wall into individual connected regions and for each such region:
-//   a. build the list of vertices (Z=0)
-//   b. take the axis into consideration and rotate the vertices.
-//   c. take layer into consideration and move the vertices.
-//   d. add the resulting vertices to the list.
-
-  private void createVertices(ArrayList<float[]> list, int[] wall, int axis, int layer)
-    {
-    int sections = markSections(wall);
-
-    for(int i=0; i<sections; i++)
-      {
-      float[] vertices = buildVertices(wall,i+1);
-
-      rotateAndMoveVertices(vertices,axis,layer);
-      list.add(vertices);
-/*
-      int len = vertices.length/3;
-      String w="";
-
-      for(int j=0; j<len; j++)
-        {
-        w += ( "["+vertices[3*j]+" "+vertices[3*j+1]+" "+vertices[3*j+2]+"] ");
-        }
-      android.util.Log.e("D", i+" vertices after: "+w);
-*/
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static boolean vertInFace(float[] vertex, float[] move, Static3D faceAxis, float dist)
-    {
-    final float MAX_ERROR = 0.01f;
-
-    float x= faceAxis.get0();
-    float y= faceAxis.get1();
-    float z= faceAxis.get2();
-
-    float a = (vertex[0]+move[0])*x + (vertex[1]+move[1])*y + (vertex[2]+move[2])*z;
-    float diff = a - dist;
-
-    return diff>-MAX_ERROR && diff<MAX_ERROR;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// (vertices,indices) define a cubit face, i.e. a connected subset of the 3x3 grid.
-// Return its 'diameter', i.e. max(width,height)
-
-  private int faceDiameter(float[][] vertices, int[] indices)
-    {
-    float maxX = -1.5f;
-    float minX = +1.5f;
-    float maxY = -1.5f;
-    float minY = +1.5f;
-    float maxZ = -1.5f;
-    float minZ = +1.5f;
-
-    for (int index : indices)
-      {
-      float[] v = vertices[index];
-
-      if (v[0] > maxX) maxX = v[0];
-      if (v[0] < minX) minX = v[0];
-      if (v[1] > maxY) maxY = v[1];
-      if (v[1] < minY) minY = v[1];
-      if (v[2] > maxZ) maxZ = v[2];
-      if (v[2] < minZ) minZ = v[2];
-      }
-
-    float diffX = maxX-minX;
-    float diffY = maxY-minY;
-    float diffZ = maxZ-minZ;
-
-    float max = diffX>diffY ? Math.max(diffX,diffZ) : Math.max(diffY,diffZ);
-
-    return (int)max;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return array of:
-// 0 if this is an inner face, 1 if its diameter is 1, 2 if diameter is 2, 3 if 3.
-
-  private int[] generateBandIndices(float[][] vertices, int[][] indices, float[] move)
-    {
-    int numCubitFaces = indices.length;
-    int[] bandIndices = new int[numCubitFaces];
-
-    for(int cubitFace=0; cubitFace<numCubitFaces; cubitFace++)
-      {
-      bandIndices[cubitFace] = 0xffffffff;
-      int numVertices = indices[cubitFace].length;
-
-      for(int vertex=0; vertex<numVertices; vertex++)
-        {
-        int vertBelongsBitmap = 0x00000000;
-
-        for(int face=0; face<6; face++)
-          {
-          float[] vert = vertices[ indices[cubitFace][vertex] ];
-          if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[face],1.5f) )
-            {
-            vertBelongsBitmap |= (1<<face);
-            }
-          }
-
-        bandIndices[cubitFace] &= vertBelongsBitmap;
-        }
-
-      if( bandIndices[cubitFace]!=0 ) // outer face
-        {
-        bandIndices[cubitFace] = faceDiameter(vertices, indices[cubitFace]);
-        }
-
-      //android.util.Log.e("D", "cubit face "+cubitFace+" : "+bandIndices[cubitFace]);
-      }
-
-    return bandIndices;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int[] generateCornerIndices(float[][] vertices, boolean roundCorners)
-    {
-    int len = vertices.length;
-    int val = roundCorners ? 0 : -1;
-    int[] cornerIndices = new int[len];
-    for(int i=0; i<len; i++) cornerIndices[i] = val;
-    return cornerIndices;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int computeVectorFace(float[] prev, float[] curr, float[] next)
-    {
-    float ax = prev[0]-curr[0];
-    float ay = prev[1]-curr[1];
-    float az = prev[2]-curr[2];
-
-    float bx = next[0]-curr[0];
-    float by = next[1]-curr[1];
-    float bz = next[2]-curr[2];
-
-    float lena = (float)Math.sqrt(ax*ax + ay*ay + az*az);
-    float lenb = (float)Math.sqrt(bx*bx + by*by + bz*bz);
-
-    ax /= lena;
-    ay /= lena;
-    az /= lena;
-
-    bx /= lenb;
-    by /= lenb;
-    bz /= lenb;
-
-    float cx = ax + bx + ay*bz-az*by;
-    float cy = ay + by + az*bx-ax*bz;
-    float cz = az + bz + ax*by-ay*bx;
-
-    return (cx<0 ? 0:4) + (cy<0 ? 0:2) + (cz<0 ? 0:1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[] computeVector(int index, float[][] vertices, int[][] indices, int[] bandIndices)
-    {
-    int band=0;
-    int len = indices.length;
-    int vector=-1;
-
-    for(int i=0; i<len; i++)
-      {
-      int len2 = indices[i].length;
-
-      for(int j=0; j<len2; j++)
-        {
-        if( indices[i][j]==index )
-          {
-          int prev = j>0 ? j-1 : len2-1;
-          int next = j<len2-1 ? j+1 : 0;
-
-          int prevIndex = indices[i][prev];
-          int currIndex = indices[i][j];
-          int nextIndex = indices[i][next];
-
-          int newVector = computeVectorFace(vertices[prevIndex],vertices[currIndex],vertices[nextIndex]);
-          if( vector!=-1 && vector!=newVector ) return null;
-
-          vector = newVector;
-          band |= bandIndices[i];
-          }
-        }
-      }
-
-    return band==0 ? null : VECTOR[vector];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[][] generateVectors(float[][] vertices, int[][] indices, int[] bandIndices)
-    {
-    int len = vertices.length;
-    float[][] vectors = new float[len][];
-
-    for(int i=0; i<len; i++)
-      {
-      vectors[i] = computeVector(i,vertices,indices,bandIndices);
-      }
-
-    return vectors;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[][] generateCenters(float[][] vertices, float[][] vectors)
-    {
-    int pointer=0,total=0;
-    int len = vertices.length;
-
-    for( float[] vector : vectors )
-      {
-      if( vector!=null ) total++;
-      }
-
-    float[][] centers = new float[total][3];
-
-    for( int i=0; i<len; i++ )
-      {
-      if( vectors[i]!=null )
-        {
-        centers[pointer][0] = vertices[i][0]+vectors[i][0];
-        centers[pointer][1] = vertices[i][1]+vectors[i][1];
-        centers[pointer][2] = vertices[i][2]+vectors[i][2];
-        pointer++;
-        }
-      }
-
-    return centers;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int[] generateCenterIndices(float[][] vectors)
-    {
-    int pointer=0;
-    int len = vectors.length;
-    int[] centerIndices = new int[len];
-
-    for(int i=0; i<len; i++)
-      {
-      if( vectors[i]==null )
-        {
-        centerIndices[i] = -1;
-        }
-      else
-        {
-        centerIndices[i] = pointer;
-        pointer++;
-        }
-      }
-
-    return centerIndices;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-
-  public static FactoryBandaged3x3Cubit getInstance()
-    {
-    if( mThis==null ) mThis = new FactoryBandaged3x3Cubit();
-    return mThis;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void prepare(int numVariants)
-    {
-    if( mVertexArray==null ) mVertexArray = new ArrayList<>();
-    mVertices= new float[numVariants][][];
-    mIndices = new int[numVariants][][];
-    mMove = new float[numVariants][3];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public ObjectShape createIrregularShape(int variant, float[] pos)
-    {
-    mVertexArray.clear();
-
-    for(int x=0; x<3; x++)
-      for(int y=0; y<3; y++)
-        for(int z=0; z<3; z++)
-          {
-          mTmp[x][y][z] = cubitExists(pos,x-1.0f,y-1.0f,z-1.0f);
-          }
-
-    createRight0(mVertexArray);
-    createRight1(mVertexArray);
-    createRight2(mVertexArray);
-
-    createLeft0(mVertexArray);
-    createLeft1(mVertexArray);
-    createLeft2(mVertexArray);
-
-    createTop0(mVertexArray);
-    createTop1(mVertexArray);
-    createTop2(mVertexArray);
-
-    createBottom0(mVertexArray);
-    createBottom1(mVertexArray);
-    createBottom2(mVertexArray);
-
-    createFront0(mVertexArray);
-    createFront1(mVertexArray);
-    createFront2(mVertexArray);
-
-    createBack0(mVertexArray);
-    createBack1(mVertexArray);
-    createBack2(mVertexArray);
-
-    float[][] verts = getVertices(mVertexArray,pos,variant);
-    int[][] inds    = getIndices(mVertexArray);
-
-    compressVerticesAndIndices(variant,verts,inds);
-
-    return new ObjectShape(mVertices[variant], mIndices[variant]);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void debug(float[][] vert, int[][] ind)
-    {
-    String vv="VERTICES: ";
-    for (float[] floats : vert)
-      {
-      vv += "\n";
-      int lenV2 = floats.length / 3;
-
-      for (int v2 = 0; v2 < lenV2; v2++)
-        {
-        vv += " {";
-        vv += (floats[3 * v2] + " ");
-        vv += (floats[3 * v2 + 1] + " ");
-        vv += (floats[3 * v2 + 2] + " ");
-        vv += "}";
-        }
-      }
-    android.util.Log.e("D", vv);
-
-    String ii="INDICES: ";
-    for (int[] ints : ind)
-      {
-      ii += "\n";
-      int lenI2 = ints.length;
-
-      for (int i2 = 0; i2 < lenI2; i2++)
-        {
-        ii += (ints[i2] + " ");
-        }
-      }
-    android.util.Log.e("D", ii);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public ObjectFaceShape createIrregularFaceShape(int variant, boolean iconMode, boolean roundCorners)
-    {
-    float height   = iconMode ? 0.001f : 0.048f;
-    int angle      = 60;
-    float R        = 0.2f;
-    float S        = 0.5f;
-    int numVertices= 5;
-    int extraI     = 0;
-    int extraV     = 0;
-
-    float[][] corners   = { {0.02f,0.12f} };
-    float[][] bands     = { {  0.001f,angle,R,S,numVertices,extraV,extraI},
-                            {height/1,angle,R,S,numVertices,extraV,extraI},
-                            {height/2,angle,R,S,numVertices,extraV,extraI},
-                            {height/3,angle,R,S,numVertices,extraV,extraI} };
-
-    int[] bandIndices   = generateBandIndices(mVertices[variant], mIndices[variant], mMove[variant]);
-    int[] cornerIndices = generateCornerIndices(mVertices[variant], roundCorners);
-    float[][] vertexVec = generateVectors(mVertices[variant], mIndices[variant], bandIndices);
-    float[][] centers   = generateCenters(mVertices[variant], vertexVec);
-    int[] centerIndices = generateCenterIndices(vertexVec);
-
-    return new ObjectFaceShape(bands,bandIndices,corners,cornerIndices,centers,centerIndices,null);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public MeshBase createMesh(float[] pos, boolean iconMode, boolean roundCorners)
-    {
-    prepare(1);
-    ObjectShape shape    = createIrregularShape(0,pos);
-    ObjectFaceShape face = createIrregularFaceShape(0,iconMode,roundCorners);
-    int[][] indices      = shape.getVertIndices();
-    int numComponents    = indices.length;
-
-    FactoryCubit factory = FactoryCubit.getInstance();
-    factory.clear();
-    factory.createNewFaceTransform(shape,null);
-    return factory.createRoundedSolid(shape,face,MESH_NICE,numComponents);
-    }
-  }
diff --git a/src/main/java/org/distorted/objectlib/helpers/FactoryBandagedCubit.java b/src/main/java/org/distorted/objectlib/helpers/FactoryBandagedCubit.java
new file mode 100644
index 00000000..8a26a454
--- /dev/null
+++ b/src/main/java/org/distorted/objectlib/helpers/FactoryBandagedCubit.java
@@ -0,0 +1,956 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.objectlib.helpers;
+
+import java.util.ArrayList;
+
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.type.Static3D;
+import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
+
+import static org.distorted.objectlib.main.TwistyObject.MESH_NICE;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class FactoryBandagedCubit
+  {
+  private static final int WALL_MARKED=0;
+  private static final int WALL_EMPTY =-1;
+
+  private static final int AXIS_XP = 0;
+  private static final int AXIS_XM = 1;
+  private static final int AXIS_YP = 2;
+  private static final int AXIS_YM = 3;
+  private static final int AXIS_ZP = 4;
+  private static final int AXIS_ZM = 5;
+
+  private static final float[][] VECTOR =
+      {
+          {-1.0f,-1.0f,-1.0f},
+          {-1.0f,-1.0f,+1.0f},
+          {-1.0f,+1.0f,-1.0f},
+          {-1.0f,+1.0f,+1.0f},
+          {+1.0f,-1.0f,-1.0f},
+          {+1.0f,-1.0f,+1.0f},
+          {+1.0f,+1.0f,-1.0f},
+          {+1.0f,+1.0f,+1.0f}
+      };
+
+  private static FactoryBandagedCubit mThis;
+  private ArrayList<float[]> mVertexArray;
+  private ArrayList<float[]> mTmpArray;
+  private float[][][] mVertices;
+  private int[][][] mIndices;
+  private float[][] mMove;
+
+  private int mX, mY, mZ, mMax;
+  private float dX, dY, dZ;
+  private int[][] mWall;
+  private int[][] mPoints;
+  private boolean[][][] mTmp;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private FactoryBandagedCubit()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] getVertices(ArrayList<float[]> list, float[] move, int variant)
+    {
+    int numMoves = move.length/3;
+    mMove[variant][0]=0.0f;
+    mMove[variant][1]=0.0f;
+    mMove[variant][2]=0.0f;
+
+    for(int m=0; m<numMoves; m++)
+      {
+      mMove[variant][0] += move[3*m  ];
+      mMove[variant][1] += move[3*m+1];
+      mMove[variant][2] += move[3*m+2];
+      }
+
+    mMove[variant][0]/=numMoves;
+    mMove[variant][1]/=numMoves;
+    mMove[variant][2]/=numMoves;
+
+    int total  = 0;
+    int length = list.size();
+    float[][] vertices = new float[length][];
+
+    for( int i=0; i<length; i++ )
+      {
+      vertices[i] = list.get(i);
+      total += vertices[i].length/3;
+      }
+
+    float[][] verts = new float[total][3];
+    int pointer = 0;
+
+    for(int i=0; i<length; i++)
+      {
+      int len = vertices[i].length/3;
+
+      for(int j=0; j<len; j++)
+        {
+        verts[pointer][0] = vertices[i][3*j  ] - mMove[variant][0];
+        verts[pointer][1] = vertices[i][3*j+1] - mMove[variant][1];
+        verts[pointer][2] = vertices[i][3*j+2] - mMove[variant][2];
+        pointer++;
+        }
+      }
+
+    return verts;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[][] getIndices(ArrayList<float[]> list)
+    {
+    int indicesSoFar=0;
+    int length = list.size();
+    int[][] indices = new int[length][];
+
+    for( int i=0; i<length; i++ )
+      {
+      float[] f = list.get(i);
+      int len = f.length/3;
+      int[] ind = new int[len];
+      for(int j=0; j<len; j++) ind[j] = (indicesSoFar++);
+      indices[i] = ind;
+      }
+
+    return indices;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void markAllVertices(float[] vertex, float[][] vertices, int[][] indices, int pointer, int variant)
+    {
+    int lenI = indices.length;
+
+    for(int index=0; index<lenI; index++)
+      {
+      int len = indices[index].length;
+
+      for(int i=0; i<len; i++)
+        {
+        if( mIndices[variant][index][i] == -1 )
+          {
+          int ind = indices[index][i];
+          float[] ver = vertices[ind];
+
+          if( vertex[0]==ver[0] && vertex[1]==ver[1] && vertex[2]==ver[2] )
+            {
+            mIndices[variant][index][i] = pointer;
+            }
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// So far the 'vertices/indices' are stored inefficiently, with each vertex stored three times
+// (each one normally is a corner of three faces) or even six times. Compress!
+// Example of six times: the central vertex here:
+//
+// { 1.0f,  0.0f, -1.0f,
+//   1.0f, -1.0f, -1.0f,
+//   1.0f, -1.0f, +0.0f,
+//   0.0f, -1.0f, -1.0f },
+
+  private void compressVerticesAndIndices(int variant, float[][] vertices, int[][] indices)
+    {
+    if( mTmpArray==null ) mTmpArray = new ArrayList<>();
+
+    int lenI = indices.length;
+    int pointer=0;
+
+    mIndices[variant] = new int[lenI][];
+
+    for(int index=0; index<lenI; index++)
+      {
+      int len = indices[index].length;
+      mIndices[variant][index] = new int[len];
+      for(int i=0; i<len; i++) mIndices[variant][index][i] = -1;
+      }
+
+    for(int index=0; index<lenI; index++)
+      {
+      int len = indices[index].length;
+
+      for(int i=0; i<len; i++)
+        {
+        if( mIndices[variant][index][i] == -1 )
+          {
+          int ind = indices[index][i];
+          float[] ver = vertices[ind];
+          mTmpArray.add(ver);
+          markAllVertices(ver,vertices,indices,pointer,variant);
+          pointer++;
+          }
+        }
+      }
+
+    int len = mTmpArray.size();
+    mVertices[variant] = new float[len][];
+
+    for(int i=0; i<len; i++)
+      {
+      mVertices[variant][i] = mTmpArray.remove(0);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean cubitExists(float[] pos, float x, float y, float z)
+    {
+    int len = pos.length/3;
+
+    for(int i=0; i<len; i++)
+      if( pos[3*i]==x && pos[3*i+1]==y && pos[3*i+2]==z ) return true;
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createRight(int x, ArrayList<float[]> list)
+    {
+    for(int y=0; y<mMax; y++)
+      for(int z=0; z<mMax; z++)
+        {
+        boolean b = (y<mY && z<mZ) && ( mTmp[x][mY-1-y][mZ-1-z] && (x+1>=mX || !mTmp[x+1][mY-1-y][mZ-1-z]) );
+        mWall[z][y] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_XP,x);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createLeft(int x, ArrayList<float[]> list)
+    {
+    for(int y=0; y<mMax; y++)
+      for(int z=0; z<mMax; z++)
+        {
+        boolean b = (y<mY && z<mZ) && ( mTmp[x][mY-1-y][z] && (x<1 || !mTmp[x-1][mY-1-y][z]) );
+        mWall[z][y] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_XM,x);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createTop(int y, ArrayList<float[]> list)
+    {
+    for(int z=0; z<mMax; z++)
+      for(int x=0; x<mMax; x++)
+        {
+        boolean b = (x<mX && z<mZ) && ( mTmp[x][y][z] && (y+1>=mY || !mTmp[x][y+1][z]) );
+        mWall[x][z] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_YP,y);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createBottom(int y, ArrayList<float[]> list)
+    {
+    for(int z=0; z<mMax; z++)
+      for(int x=0; x<mMax; x++)
+        {
+        boolean b = (x<mX && z<mZ) && ( mTmp[x][y][mZ-1-z] && (y<1 || !mTmp[x][y-1][mZ-1-z]) );
+        mWall[x][z] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_YM,y);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createFront(int z, ArrayList<float[]> list)
+    {
+    for(int y=0; y<mMax; y++)
+      for(int x=0; x<mMax; x++)
+        {
+        boolean b = (x<mX && y<mY) && ( mTmp[x][mY-1-y][z] && (z+1>=mZ || !mTmp[x][mY-1-y][z+1]) );
+        mWall[x][y] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_ZP,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createBack(int z, ArrayList<float[]> list)
+    {
+    for(int y=0; y<mMax; y++)
+      for(int x=0; x<mMax; x++)
+        {
+        boolean b = (x<mX && y<mY) && ( mTmp[mX-1-x][mY-1-y][z] && (z<1 || !mTmp[mX-1-x][mY-1-y][z-1]) );
+        mWall[x][y] = b ? WALL_MARKED : WALL_EMPTY;
+        }
+
+    createVertices(list,mWall,AXIS_ZM,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void markNeighbours(int[][] wall, int x, int y, int section)
+    {
+    wall[x][y] = section;
+
+    if( x  >   0 && wall[x-1][y]==WALL_MARKED ) markNeighbours(wall,x-1,y,section);
+    if( x+1<mMax && wall[x+1][y]==WALL_MARKED ) markNeighbours(wall,x+1,y,section);
+    if( y  >   0 && wall[x][y-1]==WALL_MARKED ) markNeighbours(wall,x,y-1,section);
+    if( y+1<mMax && wall[x][y+1]==WALL_MARKED ) markNeighbours(wall,x,y+1,section);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int markSections(int[][] wall)
+    {
+    int sections = 0;
+
+    for(int x=0; x<mMax; x++)
+      for(int y=0; y<mMax; y++)
+        if( wall[x][y]==WALL_MARKED )
+          {
+          sections++;
+          markNeighbours(wall,x,y,sections);
+          }
+
+    return sections;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return true iff exactly three or exactly one of the four values (x1,x2,x3,x4) are equal to 'value'.
+
+  private boolean threeOrOne(int x1, int x2, int x3, int x4, int value)
+    {
+    if( x1==value ) return x2==value ? (x3==value)^(x4==value) : (x3==value)^(x4!=value);
+    else            return x2==value ? (x3==value)^(x4!=value) : (x3==value)^(x4==value);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean isOddVertical(int x, int y)
+    {
+    int number = 0;
+
+    for(int i=0; i<y; i++)
+      if( mPoints[x][i]==0 ) number++;
+
+    return (number%2)==0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean isOddHorizontal(int x, int y)
+    {
+    int number = 0;
+
+    for(int i=0; i<x; i++)
+      if( mPoints[i][y]==0 ) number++;
+
+    return (number%2)==0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int moveUp(int x, int y)
+    {
+    for(int i=y-1; i>=0; i--)
+      if( mPoints[x][i]==0 ) return i;
+
+    android.util.Log.e("D", "moveUp error!");
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int moveDown(int x, int y)
+    {
+    for(int i=y+1; i<=mMax; i++)
+      if( mPoints[x][i]==0 ) return i;
+
+    android.util.Log.e("D", "moveDown error!");
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int moveLeft(int x, int y)
+    {
+    for(int i=x-1; i>=0; i--)
+      if( mPoints[i][y]==0 ) return i;
+
+    android.util.Log.e("D", "moveLeft error!");
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int moveRight(int x, int y)
+    {
+    for(int i=x+1; i<=mMax; i++)
+      if( mPoints[i][y]==0 ) return i;
+
+    android.util.Log.e("D", "moveRight error!");
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] buildVertices(int[][] wall, int section)
+    {
+    int numPoints = buildPoints(wall,section);
+    int numSections = numPoints/2;
+    float[] vertices = new float[3*numPoints];
+    int x=0,y=0,pointer=0;
+
+    for(int i=0; i<=mMax; i++)
+      for(int j=0; j<=mMax; j++)
+        if( mPoints[i][j]==0 )
+          {
+          x = i;
+          y = j;
+          i = j = mMax+1;
+          }
+
+    for(int s=0; s<numSections; s++)
+      {
+      vertices[6*pointer  ] = x-dX;
+      vertices[6*pointer+1] = dY-y;
+      vertices[6*pointer+2] = 0.0f;
+
+      y = isOddVertical(x,y) ? moveDown(x,y) : moveUp(x,y);
+
+      vertices[6*pointer+3] = x-dX;
+      vertices[6*pointer+4] = dY-y;
+      vertices[6*pointer+5] = 0.0f;
+
+      x = isOddHorizontal(x,y) ? moveRight(x,y) : moveLeft(x,y);
+
+      pointer++;
+      }
+
+    return vertices;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int buildPoints(int[][] wall, int section)
+    {
+    int numPoints=0;
+    boolean thereIsCorner;
+
+    for(int x=0; x<=mMax; x++)
+      for(int y=0; y<=mMax; y++)
+        {
+        if( y==0 )
+          {
+               if( x==   0 ) thereIsCorner = (wall[     0][0]==section);
+          else if( x==mMax ) thereIsCorner = (wall[mMax-1][0]==section);
+          else               thereIsCorner = (wall[x-1][0]==section)^(wall[x][0]==section);
+          }
+        else if( y==mMax )
+          {
+               if( x==   0 ) thereIsCorner = (wall[     0][mMax-1]==section);
+          else if( x==mMax ) thereIsCorner = (wall[mMax-1][mMax-1]==section);
+          else               thereIsCorner = (wall[x-1][mMax-1]==section)^(wall[x][mMax-1]==section);
+          }
+        else if( x==0 )
+          {
+          thereIsCorner = (wall[0][y-1]==section)^(wall[0][y]==section);
+          }
+        else if( x==mMax )
+          {
+          thereIsCorner = (wall[mMax-1][y-1]==section)^(wall[mMax-1][y]==section);
+          }
+        else
+          {
+          thereIsCorner = threeOrOne(wall[x-1][y-1],wall[x-1][y],wall[x][y-1],wall[x][y],section);
+          }
+
+        if( thereIsCorner )
+          {
+          mPoints[x][y] = 0;
+          numPoints++;
+          }
+        else
+          {
+          mPoints[x][y] =-1;
+          }
+        }
+
+    return numPoints;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void rotateAndMoveVertices(float[] vertices, int axis, int layer)
+    {
+    int i,len = vertices.length/3;
+
+    switch(axis)
+      {
+      case AXIS_XP: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i+2] = -vertices[3*i];
+                      vertices[3*i  ] = layer-(dX-1.0f);
+                      }
+                    break;
+      case AXIS_XM: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i+2] = vertices[3*i];
+                      vertices[3*i  ] = layer-dX;
+                      }
+                    break;
+      case AXIS_YP: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i+2] = -vertices[3*i+1];
+                      vertices[3*i+1] = layer-(dY-1.0f);
+                      }
+                    break;
+      case AXIS_YM: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i+2] = vertices[3*i+1];
+                      vertices[3*i+1] = layer-dY;
+                      }
+                    break;
+      case AXIS_ZP: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i+2] = layer-(dZ-1.0f);
+                      }
+                    break;
+      case AXIS_ZM: for(i=0; i<len; i++)
+                      {
+                      vertices[3*i  ] = -vertices[3*i];
+                      vertices[3*i+2] = layer-dZ;
+                      }
+                    break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 1. assume the 'wall' is in the XY plane
+// 2. split the wall into individual connected regions and for each such region:
+//   a. build the list of vertices (Z=0)
+//   b. take the axis into consideration and rotate the vertices.
+//   c. take layer into consideration and move the vertices.
+//   d. add the resulting vertices to the list.
+
+  private void createVertices(ArrayList<float[]> list, int[][] wall, int axis, int layer)
+    {
+    int sections = markSections(wall);
+
+    for(int i=0; i<sections; i++)
+      {
+      float[] vertices = buildVertices(wall,i+1);
+      rotateAndMoveVertices(vertices,axis,layer);
+      list.add(vertices);
+/*
+      int len = vertices.length/3;
+      String w="";
+
+      for(int j=0; j<len; j++)
+        {
+        w += ( "["+vertices[3*j]+" "+vertices[3*j+1]+" "+vertices[3*j+2]+"] ");
+        }
+      android.util.Log.e("D", i+" vertices after: "+w);
+*/
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static boolean vertInFace(float[] vertex, float[] move, Static3D faceAxis, float dist)
+    {
+    final float MAX_ERROR = 0.01f;
+
+    float x= faceAxis.get0();
+    float y= faceAxis.get1();
+    float z= faceAxis.get2();
+
+    float a = (vertex[0]+move[0])*x + (vertex[1]+move[1])*y + (vertex[2]+move[2])*z;
+    float diff = a - dist;
+
+    return diff>-MAX_ERROR && diff<MAX_ERROR;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// (vertices,indices) define a cubit face, i.e. a connected subset of the NxN grid.
+// Return its 'diameter', i.e. max(width,height)
+
+  private int faceDiameter(float[][] vertices, int[] indices)
+    {
+    float maxX = -dX;
+    float minX = +dX;
+    float maxY = -dY;
+    float minY = +dY;
+    float maxZ = -dZ;
+    float minZ = +dZ;
+
+    for (int index : indices)
+      {
+      float[] v = vertices[index];
+
+      if (v[0] > maxX) maxX = v[0];
+      if (v[0] < minX) minX = v[0];
+      if (v[1] > maxY) maxY = v[1];
+      if (v[1] < minY) minY = v[1];
+      if (v[2] > maxZ) maxZ = v[2];
+      if (v[2] < minZ) minZ = v[2];
+      }
+
+    float diffX = maxX-minX;
+    float diffY = maxY-minY;
+    float diffZ = maxZ-minZ;
+
+    float max = diffX>diffY ? Math.max(diffX,diffZ) : Math.max(diffY,diffZ);
+
+    return (int)max;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return array of:
+// 0 if this is an inner face, 1 if its diameter is 1, 2 if diameter is 2, 3 if 3, etc
+
+  private int[] generateBandIndices(float[][] vertices, int[][] indices, float[] move)
+    {
+    int numCubitFaces = indices.length;
+    int[] bandIndices = new int[numCubitFaces];
+
+    for(int cubitFace=0; cubitFace<numCubitFaces; cubitFace++)
+      {
+      bandIndices[cubitFace] = 0xffffffff;
+      int numVertices = indices[cubitFace].length;
+
+      for(int vertex=0; vertex<numVertices; vertex++)
+        {
+        int vertBelongsBitmap = 0x00000000;
+        float[] vert = vertices[ indices[cubitFace][vertex] ];
+
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[0], dX) ) vertBelongsBitmap |= (1<<0);
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[1], dX) ) vertBelongsBitmap |= (1<<1);
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[2], dY) ) vertBelongsBitmap |= (1<<2);
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[3], dY) ) vertBelongsBitmap |= (1<<3);
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[4], dZ) ) vertBelongsBitmap |= (1<<4);
+        if( vertInFace(vert, move, TouchControlHexahedron.FACE_AXIS[5], dZ) ) vertBelongsBitmap |= (1<<5);
+
+        bandIndices[cubitFace] &= vertBelongsBitmap;
+        }
+
+      if( bandIndices[cubitFace]!=0 ) // outer face
+        {
+        bandIndices[cubitFace] = faceDiameter(vertices, indices[cubitFace]);
+        }
+
+      //android.util.Log.e("D", "cubit face "+cubitFace+" : "+bandIndices[cubitFace]);
+      }
+
+    return bandIndices;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] generateCornerIndices(float[][] vertices, boolean roundCorners)
+    {
+    int len = vertices.length;
+    int val = roundCorners ? 0 : -1;
+    int[] cornerIndices = new int[len];
+    for(int i=0; i<len; i++) cornerIndices[i] = val;
+    return cornerIndices;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeVectorFace(float[] prev, float[] curr, float[] next)
+    {
+    float ax = prev[0]-curr[0];
+    float ay = prev[1]-curr[1];
+    float az = prev[2]-curr[2];
+
+    float bx = next[0]-curr[0];
+    float by = next[1]-curr[1];
+    float bz = next[2]-curr[2];
+
+    float lena = (float)Math.sqrt(ax*ax + ay*ay + az*az);
+    float lenb = (float)Math.sqrt(bx*bx + by*by + bz*bz);
+
+    ax /= lena;
+    ay /= lena;
+    az /= lena;
+
+    bx /= lenb;
+    by /= lenb;
+    bz /= lenb;
+
+    float cx = ax + bx + ay*bz-az*by;
+    float cy = ay + by + az*bx-ax*bz;
+    float cz = az + bz + ax*by-ay*bx;
+
+    return (cx<0 ? 0:4) + (cy<0 ? 0:2) + (cz<0 ? 0:1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] computeVector(int index, float[][] vertices, int[][] indices, int[] bandIndices)
+    {
+    int band=0;
+    int len = indices.length;
+    int vector=-1;
+
+    for(int i=0; i<len; i++)
+      {
+      int len2 = indices[i].length;
+
+      for(int j=0; j<len2; j++)
+        {
+        if( indices[i][j]==index )
+          {
+          int prev = j>0 ? j-1 : len2-1;
+          int next = j<len2-1 ? j+1 : 0;
+
+          int prevIndex = indices[i][prev];
+          int currIndex = indices[i][j];
+          int nextIndex = indices[i][next];
+
+          int newVector = computeVectorFace(vertices[prevIndex],vertices[currIndex],vertices[nextIndex]);
+          if( vector!=-1 && vector!=newVector ) return null;
+
+          vector = newVector;
+          band |= bandIndices[i];
+          }
+        }
+      }
+
+    return band==0 ? null : VECTOR[vector];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] generateVectors(float[][] vertices, int[][] indices, int[] bandIndices)
+    {
+    int len = vertices.length;
+    float[][] vectors = new float[len][];
+
+    for(int i=0; i<len; i++)
+      {
+      vectors[i] = computeVector(i,vertices,indices,bandIndices);
+      }
+
+    return vectors;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[][] generateCenters(float[][] vertices, float[][] vectors)
+    {
+    int pointer=0,total=0;
+    int len = vertices.length;
+
+    for( float[] vector : vectors )
+      {
+      if( vector!=null ) total++;
+      }
+
+    float[][] centers = new float[total][3];
+
+    for( int i=0; i<len; i++ )
+      {
+      if( vectors[i]!=null )
+        {
+        centers[pointer][0] = vertices[i][0]+vectors[i][0];
+        centers[pointer][1] = vertices[i][1]+vectors[i][1];
+        centers[pointer][2] = vertices[i][2]+vectors[i][2];
+        pointer++;
+        }
+      }
+
+    return centers;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] generateCenterIndices(float[][] vectors)
+    {
+    int pointer=0;
+    int len = vectors.length;
+    int[] centerIndices = new int[len];
+
+    for(int i=0; i<len; i++)
+      {
+      if( vectors[i]==null )
+        {
+        centerIndices[i] = -1;
+        }
+      else
+        {
+        centerIndices[i] = pointer;
+        pointer++;
+        }
+      }
+
+    return centerIndices;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public static FactoryBandagedCubit getInstance()
+    {
+    if( mThis==null ) mThis = new FactoryBandagedCubit();
+    return mThis;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void prepare(int numVariants)
+    {
+    if( mVertexArray==null ) mVertexArray = new ArrayList<>();
+    mVertices= new float[numVariants][][];
+    mIndices = new int[numVariants][][];
+    mMove = new float[numVariants][3];
+
+    mX = 3;
+    mY = 3;
+    mZ = 3;
+
+    dX = mX/2.0f;
+    dY = mY/2.0f;
+    dZ = mZ/2.0f;
+
+    mMax = mX>mY ? Math.max(mX,mZ) : Math.max(mY,mZ);
+
+    mWall   = new int[mMax][mMax];
+    mPoints = new int[mMax+1][mMax+1];
+    mTmp    = new boolean[mX][mY][mZ];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectShape createIrregularShape(int variant, float[] pos)
+    {
+    mVertexArray.clear();
+
+    for(int x=0; x<mX; x++)
+      for(int y=0; y<mY; y++)
+        for(int z=0; z<mZ; z++) mTmp[x][y][z] = cubitExists(pos,x-1.0f,y-1.0f,z-1.0f);
+
+    for(int x=0; x<mX; x++) createRight (x,mVertexArray);
+    for(int x=0; x<mX; x++) createLeft  (x,mVertexArray);
+    for(int y=0; y<mY; y++) createTop   (y,mVertexArray);
+    for(int y=0; y<mY; y++) createBottom(y,mVertexArray);
+    for(int z=0; z<mZ; z++) createFront (z,mVertexArray);
+    for(int z=0; z<mZ; z++) createBack  (z,mVertexArray);
+
+    float[][] verts = getVertices(mVertexArray,pos,variant);
+    int[][] inds    = getIndices(mVertexArray);
+
+    compressVerticesAndIndices(variant,verts,inds);
+
+    return new ObjectShape(mVertices[variant], mIndices[variant]);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void debug(float[][] vert, int[][] ind)
+    {
+    String vv="VERTICES: ";
+    for (float[] floats : vert)
+      {
+      vv += "\n";
+      int lenV2 = floats.length / 3;
+
+      for (int v2 = 0; v2 < lenV2; v2++)
+        {
+        vv += " {";
+        vv += (floats[3 * v2] + " ");
+        vv += (floats[3 * v2 + 1] + " ");
+        vv += (floats[3 * v2 + 2] + " ");
+        vv += "}";
+        }
+      }
+    android.util.Log.e("D", vv);
+
+    String ii="INDICES: ";
+    for (int[] ints : ind)
+      {
+      ii += "\n";
+      int lenI2 = ints.length;
+
+      for (int i2 = 0; i2 < lenI2; i2++)
+        {
+        ii += (ints[i2] + " ");
+        }
+      }
+    android.util.Log.e("D", ii);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public ObjectFaceShape createIrregularFaceShape(int variant, boolean iconMode, boolean roundCorners)
+    {
+    float height   = iconMode ? 0.001f : 0.048f;
+    int angle      = 60;
+    float R        = 0.2f;
+    float S        = 0.5f;
+    int numVertices= 5;
+    int extraI     = 0;
+    int extraV     = 0;
+
+    float[][] corners   = { {0.02f,0.12f} };
+    float[][] bands     = { {  0.001f,angle,R,S,numVertices,extraV,extraI},
+                            {height/1,angle,R,S,numVertices,extraV,extraI},
+                            {height/2,angle,R,S,numVertices,extraV,extraI},
+                            {height/3,angle,R,S,numVertices,extraV,extraI} };
+
+    int[] bandIndices   = generateBandIndices(mVertices[variant], mIndices[variant], mMove[variant]);
+    int[] cornerIndices = generateCornerIndices(mVertices[variant], roundCorners);
+    float[][] vertexVec = generateVectors(mVertices[variant], mIndices[variant], bandIndices);
+    float[][] centers   = generateCenters(mVertices[variant], vertexVec);
+    int[] centerIndices = generateCenterIndices(vertexVec);
+
+    return new ObjectFaceShape(bands,bandIndices,corners,cornerIndices,centers,centerIndices,null);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public MeshBase createMesh(float[] pos, boolean iconMode, boolean roundCorners)
+    {
+    prepare(1);
+    ObjectShape shape    = createIrregularShape(0,pos);
+    ObjectFaceShape face = createIrregularFaceShape(0,iconMode,roundCorners);
+    int[][] indices      = shape.getVertIndices();
+    int numComponents    = indices.length;
+
+    FactoryCubit factory = FactoryCubit.getInstance();
+    factory.clear();
+    factory.createNewFaceTransform(shape,null);
+    return factory.createRoundedSolid(shape,face,MESH_NICE,numComponents);
+    }
+  }
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
index 935bb7a2..60473572 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedAbstract.java
@@ -19,7 +19,7 @@
 
 package org.distorted.objectlib.objects;
 
-import static org.distorted.objectlib.touchcontrol.TouchControl.TC_HEXAHEDRON;
+import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CUBOID;
 import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_NOT_SPLIT;
 
 import java.io.InputStream;
@@ -28,7 +28,7 @@ import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
-import org.distorted.objectlib.helpers.FactoryBandaged3x3Cubit;
+import org.distorted.objectlib.helpers.FactoryBandagedCubit;
 import org.distorted.objectlib.helpers.ObjectFaceShape;
 import org.distorted.objectlib.scrambling.ScrambleStateBandaged3x3;
 import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
@@ -77,7 +77,7 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
 
   TwistyBandagedAbstract(int[] numL, int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream stream)
     {
-    super(numL, meshState, iconMode, numL[0], quat, move, scale, stream);
+    super(numL, meshState, iconMode, (numL[0]+numL[1]+numL[2])/3.0f, quat, move, scale, stream);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -337,7 +337,7 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
       return null;
       }
 
-    FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
+    FactoryBandagedCubit factory = FactoryBandagedCubit.getInstance();
     return factory.createIrregularShape(variant,positions[cubit]);
     }
 
@@ -392,7 +392,7 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
       return new ObjectFaceShape(bands,bandIndices,corners,cornerIndices,centers,centerIndices,null);
       }
 
-    FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
+    FactoryBandagedCubit factory = FactoryBandagedCubit.getInstance();
     return factory.createIrregularFaceShape(variant, isInIconMode(), roundCorners );
     }
 
@@ -467,7 +467,7 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
         }
       }
 
-    FactoryBandaged3x3Cubit factory = FactoryBandaged3x3Cubit.getInstance();
+    FactoryBandagedCubit factory = FactoryBandagedCubit.getInstance();
     factory.prepare(numVariants);
 
     return numVariants;
@@ -484,20 +484,20 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
 
   public float[][] getCuts(int[] numLayers)
     {
-    int numL = numLayers[0];
-
-    if( numL<2 ) return null;
-
     if( mCuts==null )
       {
-      mCuts = new float[3][numL-1];
+      mCuts = new float[3][];
 
-      for(int i=0; i<numL-1; i++)
+      for(int axis=0; axis<3; axis++)
         {
-        float cut = (2-numL)*0.5f + i;
-        mCuts[0][i] = cut;
-        mCuts[1][i] = cut;
-        mCuts[2][i] = cut;
+        int len = numLayers[axis];
+        float start = (2-len)*0.5f;
+
+        if( len>=2 )
+          {
+          mCuts[axis] = new float[len-1];
+          for(int i=0; i<len-1; i++) mCuts[axis][i] = start+i;
+          }
         }
       }
 
@@ -524,7 +524,7 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
 
   public int getTouchControlType()
     {
-    return TC_HEXAHEDRON;
+    return TC_CUBOID;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -545,7 +545,12 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
 
   public float[] getDist3D(int[] numLayers)
     {
-    return TouchControlHexahedron.D3D;
+    float x = numLayers[0];
+    float y = numLayers[1];
+    float z = numLayers[2];
+    float a = (x+y+z)/1.5f;
+
+    return new float[] {x/a,x/a,y/a,y/a,z/a,z/a};
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -588,12 +593,25 @@ abstract class TwistyBandagedAbstract extends ShapeHexahedron
 
   public int[][] getBasicAngles()
     {
-    if( mBasicAngle ==null )
+     if( mBasicAngle==null )
       {
-      int num = getNumLayers()[0];
-      int[] tmp = new int[num];
-      for(int i=0; i<num; i++) tmp[i] = 4;
-      mBasicAngle = new int[][] { tmp,tmp,tmp };
+      int[] num = getNumLayers();
+      int numX = num[0];
+      int numY = num[1];
+      int numZ = num[2];
+
+      int x = numY==numZ ? 4 : 2;
+      int y = numX==numZ ? 4 : 2;
+      int z = numX==numY ? 4 : 2;
+
+      int[] tmpX = new int[numX];
+      for(int i=0; i<numX; i++) tmpX[i] = x;
+      int[] tmpY = new int[numY];
+      for(int i=0; i<numY; i++) tmpY[i] = y;
+      int[] tmpZ = new int[numZ];
+      for(int i=0; i<numZ; i++) tmpZ[i] = z;
+
+      mBasicAngle = new int[][] { tmpX,tmpY,tmpZ };
       }
 
     return mBasicAngle;
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
index df726a58..bbb37be2 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyBandagedGeneric.java
@@ -40,14 +40,7 @@ public class TwistyBandagedGeneric extends TwistyBandagedAbstract
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public TwistyBandagedGeneric(Static4D quat, float scale, int iconMode)
-    {
-    super( new int[] {3,3,3}, MESH_NICE, iconMode, quat, new Static3D(0,0,0), scale, null);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Computing scramble states of many a bandaged 3x3 takes way too long time and too much space.
+// Computing scramble states of many a bandaged cubes takes way too long time and too much space.
 // Return null here and turn to construction of scramble tables just-in-time.
 
   @Override
diff --git a/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java b/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
index 61239b3d..5c463917 100644
--- a/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
+++ b/src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
@@ -587,17 +587,12 @@ public class TwistyCuboid extends ShapeHexahedron
 
   public float[] getDist3D(int[] numLayers)
     {
-    float avg = (numLayers[0]+numLayers[1]+numLayers[2])/3.0f;
+    float x = numLayers[0];
+    float y = numLayers[1];
+    float z = numLayers[2];
+    float a = (x+y+z)/1.5f;
 
-    return new float[]
-        {
-        0.5f*numLayers[0]/avg,
-        0.5f*numLayers[0]/avg,
-        0.5f*numLayers[1]/avg,
-        0.5f*numLayers[1]/avg,
-        0.5f*numLayers[2]/avg,
-        0.5f*numLayers[2]/avg,
-        };
+    return new float[] {x/a,x/a,y/a,y/a,z/a,z/a};
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
