commit b1f2ccf582da2d340085e6e21f56c44e566f9a7f
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Apr 14 16:00:30 2021 +0200

    Convert the first object, the Cube, to the new Cubit-creating engine.

diff --git a/src/main/java/org/distorted/helpers/FactoryCubit.java b/src/main/java/org/distorted/helpers/FactoryCubit.java
index 55cffb57..5656b50c 100644
--- a/src/main/java/org/distorted/helpers/FactoryCubit.java
+++ b/src/main/java/org/distorted/helpers/FactoryCubit.java
@@ -19,6 +19,9 @@
 
 package org.distorted.helpers;
 
+import org.distorted.library.effect.MatrixEffectMove;
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.effect.MatrixEffectScale;
 import org.distorted.library.effect.VertexEffect;
 import org.distorted.library.effect.VertexEffectDeform;
 import org.distorted.library.effect.VertexEffectMove;
@@ -31,6 +34,8 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import java.util.ArrayList;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class FactoryCubit
@@ -63,6 +68,30 @@ public class FactoryCubit
   public static final float DIHEDRAL2= (float)((180/Math.PI)*Math.asin((2*SIN54*SIN54-1)/COS54) - 90);
   public static final float MINX_SC  = 0.5f;
 
+
+  private static final double[] mBuffer = new double[3];
+  private static final double[] mQuat1  = new double[4];
+  private static final double[] mQuat2  = new double[4];
+  private static final double[] mQuat3  = new double[4];
+  private static final double[] mQuat4  = new double[4];
+
+  private static class StickerCoords
+    {
+    double[] vertices;
+    }
+
+  private static class FaceTransform
+    {
+    int sticker;
+    double vx,vy,vz;
+    double scale;
+    double qx,qy,qz,qw;
+    boolean flip;
+    }
+
+  private static final ArrayList<FaceTransform> mFaceTransform = new ArrayList<>();
+  private static final ArrayList<StickerCoords> mStickerCoords = new ArrayList<>();
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private FactoryCubit()
@@ -208,42 +237,6 @@ public class FactoryCubit
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesCube(int sizeIndex)
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float E = 0.5f;
-    int extraI, extraV, num;
-
-    switch(sizeIndex)
-      {
-      case 0 : num = 6; extraI = 2; extraV = 2; break;
-      case 1 : num = 5; extraI = 2; extraV = 2; break;
-      case 2 : num = 5; extraI = 1; extraV = 2; break;
-      default: num = 4; extraI = 1; extraV = 1; break;
-      }
-
-    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
-    float[] bands = computeBands(0.048f,35,E,0.7f,num);
-
-    meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
-    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] = meshes[0].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[0].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[0].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createFacesSkewbCorner()
@@ -902,34 +895,6 @@ public class FactoryCubit
 // EFFECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  VertexEffect[] createVertexEffectsCube()
-    {
-    Static3D axisY   = new Static3D(0,1,0);
-    Static3D axisX   = new Static3D(1,0,0);
-    Static3D center  = new Static3D(0,0,0);
-    Static1D angle90 = new Static1D(90);
-    Static1D angle180= new Static1D(180);
-    Static1D angle270= new Static1D(270);
-
-    VertexEffect[] effect = new VertexEffect[6];
-
-    effect[0] = new VertexEffectMove(new Static3D(0,0,+0.5f));
-    effect[1] = new VertexEffectRotate( angle180, axisX, center );
-    effect[2] = new VertexEffectRotate( angle90 , axisX, center );
-    effect[3] = new VertexEffectRotate( angle270, axisX, center );
-    effect[4] = new VertexEffectRotate( angle270, axisY, center );
-    effect[5] = new VertexEffectRotate( angle90 , axisY, center );
-
-    effect[0].setMeshAssociation(63,-1);  // all 6 sides
-    effect[1].setMeshAssociation(32,-1);  // back
-    effect[2].setMeshAssociation( 8,-1);  // bottom
-    effect[3].setMeshAssociation( 4,-1);  // top
-    effect[4].setMeshAssociation( 2,-1);  // left
-    effect[5].setMeshAssociation( 1,-1);  // right
-
-    return effect;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   VertexEffect[] createVertexEffectsSkewbCorner()
@@ -1691,31 +1656,7 @@ public class FactoryCubit
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // OBJECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// CUBE
-
-  public MeshBase createCubeMesh(int index)
-    {
-    MeshBase mesh = createFacesCube(index);
-    VertexEffect[] effects = createVertexEffectsCube();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    Static3D roundingCenter  = new Static3D(0,0,0);
-    Static3D[] vertices = new Static3D[8];
-    vertices[0] = new Static3D(+0.5f,+0.5f,+0.5f);
-    vertices[1] = new Static3D(+0.5f,+0.5f,-0.5f);
-    vertices[2] = new Static3D(+0.5f,-0.5f,+0.5f);
-    vertices[3] = new Static3D(+0.5f,-0.5f,-0.5f);
-    vertices[4] = new Static3D(-0.5f,+0.5f,+0.5f);
-    vertices[5] = new Static3D(-0.5f,+0.5f,-0.5f);
-    vertices[6] = new Static3D(-0.5f,-0.5f,+0.5f);
-    vertices[7] = new Static3D(-0.5f,-0.5f,-0.5f);
-
-    roundCorners(mesh,roundingCenter,vertices,0.06f,0.12f);
-
-    mesh.mergeEffComponents();
 
-    return mesh;
-    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // SKEWB
@@ -2204,4 +2145,680 @@ public class FactoryCubit
 
     return mesh;
     }
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean areColinear(double[][] vertices, int index1, int index2, int index3)
+    {
+    double x1 = vertices[index1][0];
+    double y1 = vertices[index1][1];
+    double z1 = vertices[index1][2];
+    double x2 = vertices[index2][0];
+    double y2 = vertices[index2][1];
+    double z2 = vertices[index2][2];
+    double x3 = vertices[index3][0];
+    double y3 = vertices[index3][1];
+    double z3 = vertices[index3][2];
+
+    double v1x = x2-x1;
+    double v1y = y2-y1;
+    double v1z = z2-z1;
+    double v2x = x3-x1;
+    double v2y = y3-y1;
+    double v2z = z3-z1;
+
+    double A = Math.sqrt( (v1x*v1x+v1y*v1y+v1z*v1z) / (v2x*v2x+v2y*v2y+v2z*v2z) );
+
+    return (v1x==A*v2x && v1y==A*v2y && v1z==A*v2z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeNormalVector(double[][] vertices, int index1, int index2, int index3)
+    {
+    double x1 = vertices[index1][0];
+    double y1 = vertices[index1][1];
+    double z1 = vertices[index1][2];
+    double x2 = vertices[index2][0];
+    double y2 = vertices[index2][1];
+    double z2 = vertices[index2][2];
+    double x3 = vertices[index3][0];
+    double y3 = vertices[index3][1];
+    double z3 = vertices[index3][2];
+
+    double v1x = x2-x1;
+    double v1y = y2-y1;
+    double v1z = z2-z1;
+    double v2x = x3-x1;
+    double v2y = y3-y1;
+    double v2z = z3-z1;
+
+    mBuffer[0] = v1y*v2z - v2y*v1z;
+    mBuffer[1] = v1z*v2x - v2z*v1x;
+    mBuffer[2] = v1x*v2y - v2x*v1y;
+
+    double len = mBuffer[0]*mBuffer[0] + mBuffer[1]*mBuffer[1] + mBuffer[2]*mBuffer[2];
+    len = Math.sqrt(len);
+    mBuffer[0] /= len;
+    mBuffer[1] /= len;
+    mBuffer[2] /= len;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return quat1*quat2
+
+  private static void quatMultiply( double[] quat1, double[] quat2, double[] result )
+    {
+    double qx = quat1[0];
+    double qy = quat1[1];
+    double qz = quat1[2];
+    double qw = quat1[3];
+
+    double rx = quat2[0];
+    double ry = quat2[1];
+    double rz = quat2[2];
+    double rw = quat2[3];
+
+    result[0] = rw*qx - rz*qy + ry*qz + rx*qw;
+    result[1] = rw*qy + rz*qx + ry*qw - rx*qz;
+    result[2] = rw*qz + rz*qw - ry*qx + rx*qy;
+    result[3] = rw*qw - rz*qz - ry*qy - rx*qx;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fitInSquare(FaceTransform info, double[][] vert3D)
+    {
+    double minX = Double.MAX_VALUE;
+    double maxX =-Double.MAX_VALUE;
+    double minY = Double.MAX_VALUE;
+    double maxY =-Double.MAX_VALUE;
+
+    for (double[] vert : vert3D)
+      {
+      double x = vert[0];
+      double y = vert[1];
+
+      if (x > maxX) maxX = x;
+      if (x < minX) minX = x;
+      if (y > maxY) maxY = y;
+      if (y < minY) minY = y;
+      }
+
+    minX = minX<0 ? -minX:minX;
+    maxX = maxX<0 ? -maxX:maxX;
+    minY = minY<0 ? -minY:minY;
+    maxY = maxY<0 ? -maxY:maxY;
+
+    double max1 = Math.max(minX,minY);
+    double max2 = Math.max(maxX,maxY);
+    double max3 = Math.max(max1,max2);
+
+    info.scale = max3/0.5;
+
+    int len = vert3D.length;
+    StickerCoords sInfo = new StickerCoords();
+    sInfo.vertices = new double[2*len];
+
+    for( int vertex=0; vertex<len; vertex++ )
+      {
+      sInfo.vertices[2*vertex  ] = vert3D[vertex][0] / info.scale;
+      sInfo.vertices[2*vertex+1] = vert3D[vertex][1] / info.scale;
+      }
+
+    mStickerCoords.add(sInfo);
+
+    info.sticker = mStickerCoords.size() -1;
+    info.flip = false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void constructNew(FaceTransform info, final double[][] vert3D)
+    {
+    // compute center of gravity
+    info.vx = 0.0f;
+    info.vy = 0.0f;
+    info.vz = 0.0f;
+    int len = vert3D.length;
+
+    for (double[] vert : vert3D)
+      {
+      info.vx += vert[0];
+      info.vy += vert[1];
+      info.vz += vert[2];
+      }
+
+    info.vx /= len;
+    info.vy /= len;
+    info.vz /= len;
+
+    // move all vertices so that their center of gravity is at (0,0,0)
+    for (int i=0; i<len; i++)
+      {
+      vert3D[i][0] -= info.vx;
+      vert3D[i][1] -= info.vy;
+      vert3D[i][2] -= info.vz;
+      }
+
+    // find 3 non-colinear vertices
+    int foundIndex = -1;
+
+    for(int vertex=2; vertex<len; vertex++)
+      {
+      if( !areColinear(vert3D,0,1,vertex) )
+        {
+        foundIndex = vertex;
+        break;
+        }
+      }
+
+    // compute the normal vector
+    if( foundIndex==-1 )
+      {
+      throw new RuntimeException("all vertices colinear");
+      }
+
+    computeNormalVector(vert3D,0,1,foundIndex);
+
+    // rotate so that the normal vector becomes (0,0,1)
+    double axisX, axisY, axisZ;
+
+    if( mBuffer[0]!=0.0f || mBuffer[1]!=0.0f )
+      {
+      axisX = -mBuffer[1];
+      axisY =  mBuffer[0];
+      axisZ = 0.0f;
+
+      double axiLen = axisX*axisX + axisY*axisY;
+      axiLen = Math.sqrt(axiLen);
+      axisX /= axiLen;
+      axisY /= axiLen;
+      axisZ /= axiLen;
+      }
+    else
+      {
+      axisX = 0.0f;
+      axisY = 1.0f;
+      axisZ = 0.0f;
+      }
+
+    double cosTheta = mBuffer[2];
+    double sinTheta = Math.sqrt(1-cosTheta*cosTheta);
+    double sinHalfTheta = computeSinHalf(cosTheta);
+    double cosHalfTheta = computeCosHalf(sinTheta,cosTheta);
+
+    mQuat1[0] = axisX*sinHalfTheta;
+    mQuat1[1] = axisY*sinHalfTheta;
+    mQuat1[2] = axisZ*sinHalfTheta;
+    mQuat1[3] = cosHalfTheta;
+    mQuat2[0] =-axisX*sinHalfTheta;
+    mQuat2[1] =-axisY*sinHalfTheta;
+    mQuat2[2] =-axisZ*sinHalfTheta;
+    mQuat2[3] = cosHalfTheta;
+
+    for (double[] vert : vert3D)
+      {
+      quatMultiply(mQuat1, vert  , mQuat3);
+      quatMultiply(mQuat3, mQuat2, vert  );
+      }
+
+    // fit the whole thing in a square and remember the scale & 2D vertices
+    fitInSquare(info, vert3D);
+
+    // remember the rotation
+    info.qx =-mQuat1[0];
+    info.qy =-mQuat1[1];
+    info.qz =-mQuat1[2];
+    info.qw = mQuat1[3];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private double computeCos(double oldX, double oldY, double newX, double newY, double len1, double len2)
+    {
+    double ret= (oldX*newX+oldY*newY) / (len1*len2);
+    if( ret<-1.0 ) return -1.0;
+    if( ret> 1.0 ) return  1.0;
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// sin of (signed!) angle between vectors 'old' and 'new', counterclockwise!
+
+  private double computeSin(double oldX, double oldY, double newX, double newY, double len1, double len2)
+    {
+    double ret= (newX*oldY-oldX*newY) / (len1*len2);
+    if( ret<-1.0 ) return -1.0;
+    if( ret> 1.0 ) return  1.0;
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void rotateAllVertices(double[] result, int len, double[] vertices, double sin, double cos)
+    {
+    for(int i=0; i<len; i++)
+      {
+      result[2*i  ] = vertices[2*i  ]*cos - vertices[2*i+1]*sin;
+      result[2*i+1] = vertices[2*i  ]*sin + vertices[2*i+1]*cos;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private double computeScale(double[] v1, double[] v2)
+    {
+    double lenSq1 = v1[0]*v1[0] + v1[1]*v1[1];
+    double lenSq2 = v2[0]*v2[0] + v2[1]*v2[1];
+
+    return Math.sqrt(lenSq2/lenSq1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private double computeSinHalf(double cos)
+    {
+    return Math.sqrt((1-cos)/2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private double computeCosHalf(double sin, double cos)
+    {
+    double cosHalf = Math.sqrt((1+cos)/2);
+    return sin<0 ? -cosHalf : cosHalf;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeRotatedIndex(int oldVertex, int len, int rotatedVertex, boolean inverted)
+    {
+    int v = (rotatedVertex + (inverted? -oldVertex : oldVertex));
+    if( v>=len ) v-=len;
+    if( v< 0   ) v+=len;
+
+    return v;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean isScaledVersionOf(double[] newVert, double[] oldVert, int len, int vertex, boolean inverted)
+    {
+    double EPSILON = 0.001;
+    double scale = computeScale(newVert,oldVert);
+
+    for(int i=1; i<len; i++)
+      {
+      int index = computeRotatedIndex(i,len,vertex,inverted);
+
+      double horz = oldVert[2*i  ] - scale*newVert[2*index  ];
+      double vert = oldVert[2*i+1] - scale*newVert[2*index+1];
+
+      if( horz>EPSILON || horz<-EPSILON || vert>EPSILON || vert<-EPSILON ) return false;
+      }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void mirrorAllVertices(double[] output, int len, double[] input)
+    {
+    for(int vertex=0; vertex<len; vertex++)
+      {
+      output[2*vertex  ] = input[2*vertex  ];
+      output[2*vertex+1] =-input[2*vertex+1];
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void correctInfo(FaceTransform info, double scale, double sin, double cos, int oldSticker, boolean flip)
+    {
+    mStickerCoords.remove(info.sticker);
+
+    info.flip    = flip;
+    info.sticker = oldSticker;
+    info.scale  *= scale;
+
+    mQuat1[0] = info.qx;
+    mQuat1[1] = info.qy;
+    mQuat1[2] = info.qz;
+    mQuat1[3] = info.qw;
+
+    double sinHalf = computeSinHalf(cos);
+    double cosHalf = computeCosHalf(sin,cos);
+
+    if( flip )
+      {
+      mQuat3[0] = 0.0f;
+      mQuat3[1] = 0.0f;
+      mQuat3[2] = sinHalf;
+      mQuat3[3] = cosHalf;
+
+      mQuat4[0] = 1.0;
+      mQuat4[1] = 0.0;
+      mQuat4[2] = 0.0;
+      mQuat4[3] = 0.0;
+
+      quatMultiply( mQuat3, mQuat4, mQuat2 );
+      }
+    else
+      {
+      mQuat2[0] = 0.0f;
+      mQuat2[1] = 0.0f;
+      mQuat2[2] = sinHalf;
+      mQuat2[3] = cosHalf;
+      }
+
+    quatMultiply( mQuat1, mQuat2, mQuat3 );
+
+    info.qx = mQuat3[0];
+    info.qy = mQuat3[1];
+    info.qz = mQuat3[2];
+    info.qw = mQuat3[3];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void printVert(double[] buffer)
+    {
+    int len = buffer.length/2;
+    String str = "";
+
+    for(int i=0; i<len; i++)
+      {
+      str += (" ("+buffer[2*i]+" , "+buffer[2*i+1]+" ) ");
+      }
+
+    android.util.Log.d("D", str);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean foundVertex(FaceTransform info, double[] buffer, int len, double[] newVert,
+                              double[] oldVert, double lenFirstOld, int oldSticker, boolean inverted)
+    {
+    for(int vertex=0; vertex<len; vertex++)
+      {
+      double newX = newVert[2*vertex  ];
+      double newY = newVert[2*vertex+1];
+      double lenIthNew = Math.sqrt(newX*newX + newY*newY);
+      double cos = computeCos( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
+      double sin = computeSin( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
+
+      rotateAllVertices(buffer,len,newVert,sin,cos);
+
+      if( isScaledVersionOf(buffer,oldVert,len,vertex,inverted) )
+        {
+        double scale = computeScale(oldVert,newVert);
+        correctInfo(info,scale,sin,cos,oldSticker,inverted);
+        return true;
+        }
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean successfullyCollapsedStickers(final FaceTransform newInfo, final FaceTransform oldInfo)
+    {
+    StickerCoords sNewInfo = mStickerCoords.get(newInfo.sticker);
+    StickerCoords sOldInfo = mStickerCoords.get(oldInfo.sticker);
+    double[] newVert = sNewInfo.vertices;
+    double[] oldVert = sOldInfo.vertices;
+    int oldLen = oldVert.length;
+    int newLen = newVert.length;
+
+    if( oldLen == newLen )
+      {
+      int oldSticker = oldInfo.sticker;
+      double[] buffer1 = new double[oldLen];
+      double lenFirstOld = Math.sqrt(oldVert[0]*oldVert[0] + oldVert[1]*oldVert[1]);
+      if( foundVertex(newInfo, buffer1, oldLen/2, newVert, oldVert, lenFirstOld, oldSticker, false) ) return true;
+      double[] buffer2 = new double[oldLen];
+      mirrorAllVertices(buffer2, newLen/2, newVert);
+      if( foundVertex(newInfo, buffer1, oldLen/2, buffer2, oldVert, lenFirstOld, oldSticker, true ) ) return true;
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private double[][] constructVert(double[][] vertices, int[] index)
+    {
+    int len = index.length;
+    double[][] ret = new double[len][4];
+
+    for(int i=0; i<len; i++)
+      {
+      ret[i][0] = vertices[index[i]][0];
+      ret[i][1] = vertices[index[i]][1];
+      ret[i][2] = vertices[index[i]][2];
+      ret[i][3] = 1.0f;
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void prepareAndRoundCorners(MeshBase mesh, double[][] vertices, int[][] vertIndexes,
+                                      float[][] corners, int[] cornerIndexes )
+    {
+    int numNeig, lenFV;
+    int lenV = vertices.length;
+    int[] verts = new int[2*(lenV-1)];
+    Static3D[] staticVert = new Static3D[1];
+    Static3D center = new Static3D(0,0,0);
+    double cx, cy, cz;
+    double[] singleV;
+
+    for(int v=0; v<lenV; v++)
+      {
+      // prepare verts[]
+      numNeig = 0;
+
+      for (int[] vertIndex : vertIndexes)
+        {
+        lenFV = vertIndex.length;
+
+        for (int fv = 0; fv < lenFV; fv++)
+          if (vertIndex[fv] == v)
+            {
+            int prev = fv > 0 ? fv - 1 : lenFV - 1;
+            int next = fv < lenFV - 1 ? fv + 1 : 0;
+
+            verts[numNeig++] = vertIndex[prev];
+            verts[numNeig++] = vertIndex[next];
+            }
+        }
+
+      cx=cy=cz=0.0f;
+
+      // from verts[] prepare center
+      for(int n=0; n<numNeig; n++)
+        {
+        singleV = vertices[verts[n]];
+
+        cx += singleV[0];
+        cy += singleV[1];
+        cz += singleV[2];
+        }
+      center.set( (float)(cx/numNeig - vertices[v][0]),
+                  (float)(cy/numNeig - vertices[v][1]),
+                  (float)(cz/numNeig - vertices[v][2]));
+
+android.util.Log.e("D", "vertex: "+v+" CENTER: "+center.get0()+" "+center.get1()+" "+center.get2());
+
+      // round Corners
+      staticVert[0] = new Static3D( (float)vertices[v][0], (float)vertices[v][1], (float)vertices[v][2]);
+
+      int corn = cornerIndexes[v];
+      float strength = corners[corn][0];
+      float radius   = corners[corn][1];
+
+      roundCorners(mesh, center, staticVert, strength, radius);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void correctComponents(MeshBase mesh, int numComponents)
+    {
+    int numTexToBeAdded = numComponents-mesh.getNumTexComponents();
+
+    mesh.mergeEffComponents();
+
+    for(int i=0; i<numTexToBeAdded; i++ ) mesh.addEmptyTexComponent();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC
+
+  public void printStickerCoords()
+    {
+    int stickers = mStickerCoords.size();
+
+    android.util.Log.d("D", "---- STICKER COORDS ----");
+
+    for(int s=0; s<stickers; s++)
+      {
+      String ver = "{ ";
+      StickerCoords info = mStickerCoords.get(s);
+      int len = info.vertices.length/2;
+
+      for(int i =0; i<len; i++)
+        {
+        if( i!=0 ) ver += ", ";
+        ver += ( (float)info.vertices[2*i]+"f, "+(float)info.vertices[2*i+1]+"f");
+        }
+
+      ver += " }";
+      android.util.Log.d("D", ver);
+      }
+
+    android.util.Log.d("D", "---- END STICKER COORDS ----");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void printFaceTransform()
+    {
+    android.util.Log.d("D", "---- FACE TRANSFORM ---");
+
+    int faces = mFaceTransform.size();
+
+    for(int f=0; f<faces; f++)
+      {
+      FaceTransform info = mFaceTransform.get(f);
+
+      android.util.Log.e("D", "q=("+info.qx+", "+info.qy+", "+info.qz+", "+info.qw+") v=("
+                       +info.vx+", "+info.vy+", "+info.vz+") scale="+info.scale+" sticker="+info.sticker);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void clear()
+    {
+    mStickerCoords.clear();
+    mFaceTransform.clear();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void createNewFaceTransform( final double[][] vertices, final int[][] indexes)
+    {
+    mFaceTransform.clear();
+
+    int numFaces = indexes.length;
+    FaceTransform oldInfo;
+
+    for(int face=0; face<numFaces; face++)
+      {
+      FaceTransform newInfo = new FaceTransform();
+      int[] index = indexes[face];
+      double[][] vert = constructVert(vertices,index);
+      constructNew(newInfo,vert);
+
+      for(int previous=0; previous<face; previous++)
+        {
+        oldInfo = mFaceTransform.get(previous);
+        if( successfullyCollapsedStickers(newInfo,oldInfo) ) break;
+        }
+
+      mFaceTransform.add(newInfo);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public MeshBase createRoundedSolid(final double[][] vertices, final int[][] vertIndexes,
+                                     final float[][] bands    , final int[]   bandIndexes,
+                                     final float[][] corners  , final int[]   cornerIndexes,
+                                     final int numComponents )
+    {
+    int numFaces = vertIndexes.length;
+    float[] band, bandsComputed;
+    MeshBase[] meshes = new MeshBase[numFaces];
+    FaceTransform fInfo;
+    StickerCoords sInfo;
+
+    for(int face=0; face<numFaces; face++)
+      {
+      fInfo = mFaceTransform.get(face);
+      sInfo = mStickerCoords.get(fInfo.sticker);
+
+      double[] verts = sInfo.vertices;
+      int lenVerts = verts.length;
+      float[] vertsFloat = new float[lenVerts];
+      for(int i=0; i<lenVerts; i++) vertsFloat[i] = (float)verts[i];
+
+      band = bands[bandIndexes[face]];
+      bandsComputed = computeBands( band[0], (int)band[1], band[2], band[3], (int)band[4]);
+      meshes[face] = new MeshPolygon(vertsFloat,bandsComputed,(int)band[5],(int)band[6]);
+      meshes[face].setEffectAssociation(0,(1<<face),0);
+      }
+
+    MeshBase mesh = new MeshJoined(meshes);
+    Static3D center = new Static3D(0,0,0);
+
+    for(int face=0; face<numFaces; face++)
+      {
+      int assoc = (1<<face);
+      fInfo = mFaceTransform.get(face);
+
+      float vx = (float)fInfo.vx;
+      float vy = (float)fInfo.vy;
+      float vz = (float)fInfo.vz;
+      float sc = (float)fInfo.scale;
+      float qx = (float)fInfo.qx;
+      float qy = (float)fInfo.qy;
+      float qz = (float)fInfo.qz;
+      float qw = (float)fInfo.qw;
+
+      Static3D scale = new Static3D(sc,sc, fInfo.flip ? -sc : sc);
+      Static3D move3D= new Static3D(vx,vy,vz);
+      Static4D quat  = new Static4D(qx,qy,qz,qw);
+
+      mesh.apply(new MatrixEffectScale(scale)           ,assoc,-1);
+      mesh.apply(new MatrixEffectQuaternion(quat,center),assoc,-1);
+      mesh.apply(new MatrixEffectMove(move3D)           ,assoc,-1);
+      }
+
+    prepareAndRoundCorners(mesh, vertices, vertIndexes, corners, cornerIndexes);
+
+    correctComponents(mesh,numComponents);
+
+    return mesh;
+    }
   }
diff --git a/src/main/java/org/distorted/objects/TwistyBandaged2Bar.java b/src/main/java/org/distorted/objects/TwistyBandaged2Bar.java
index 1ac8bcba..f3f7072f 100644
--- a/src/main/java/org/distorted/objects/TwistyBandaged2Bar.java
+++ b/src/main/java/org/distorted/objects/TwistyBandaged2Bar.java
@@ -59,6 +59,33 @@ class TwistyBandaged2Bar extends TwistyBandagedAbstract
        { 0.0f, -1.0f, -1.0f}
       };
 
+  private final double[][] VERTICES_SMALL = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+  private final double[][] VERTICES_BIG = new double[][]
+          {
+              // TODO
+          };
+
+  private final int[][] VERT_INDEXES = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
   private static final int[] QUAT_INDICES = new int[] { 2 };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -69,6 +96,36 @@ class TwistyBandaged2Bar extends TwistyBandagedAbstract
     super(size, quat, texture, mesh, effects, moves, ObjectList.BAN2, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+     if( cubitType==0 )  // Small cubit
+      {
+      return VERTICES_SMALL;
+      }
+    if( cubitType==1 )  // Big cubit
+      {
+      return VERTICES_BIG;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float[][] getPositions()
diff --git a/src/main/java/org/distorted/objects/TwistyBandaged3Plate.java b/src/main/java/org/distorted/objects/TwistyBandaged3Plate.java
index 86935225..bfbca51b 100644
--- a/src/main/java/org/distorted/objects/TwistyBandaged3Plate.java
+++ b/src/main/java/org/distorted/objects/TwistyBandaged3Plate.java
@@ -95,6 +95,33 @@ class TwistyBandaged3Plate extends TwistyBandagedAbstract
        { 0.0f,  0.0f, -1.0f}
       };
 
+  private final double[][] VERTICES_SMALL = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+  private final double[][] VERTICES_BIG = new double[][]
+          {
+              // TODO
+          };
+
+  private final int[][] VERT_INDEXES = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
   private static final int[] QUAT_INDICES = new int[] { 1, 3 };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -105,6 +132,36 @@ class TwistyBandaged3Plate extends TwistyBandagedAbstract
     super(size, quat, texture, mesh, effects, moves, ObjectList.BAN3, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+     if( cubitType==0 )  // Small cubit
+      {
+      return VERTICES_SMALL;
+      }
+    if( cubitType==1 )  // Big cubit
+      {
+      return VERTICES_BIG;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float[][] getPositions()
diff --git a/src/main/java/org/distorted/objects/TwistyBandagedEvil.java b/src/main/java/org/distorted/objects/TwistyBandagedEvil.java
index 15297e3e..6baa6697 100644
--- a/src/main/java/org/distorted/objects/TwistyBandagedEvil.java
+++ b/src/main/java/org/distorted/objects/TwistyBandagedEvil.java
@@ -209,6 +209,38 @@ class TwistyBandagedEvil extends TwistyBandagedAbstract
         { 1.0f, -1.0f, -1.0f,  1.0f,  0.0f, -1.0f}
       };
 
+  private final double[][] VERTICES_SMALL = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+  private final double[][] VERTICES_MEDIUM = new double[][]
+          {
+              // TODO
+          };
+
+  private final double[][] VERTICES_BIG = new double[][]
+          {
+              // TODO
+          };
+
+  private final int[][] VERT_INDEXES = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
   private static final int[] QUAT_INDICES = new int[]
       { 0, 1, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 3 };
 
@@ -220,6 +252,40 @@ class TwistyBandagedEvil extends TwistyBandagedAbstract
     super(size, quat, texture, mesh, effects, moves, ObjectList.BAN4, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+     if( cubitType==0 )  // Small cubit
+      {
+      return VERTICES_SMALL;
+      }
+    if( cubitType==1 )  // Medium cubit
+      {
+      return VERTICES_MEDIUM;
+      }
+    if( cubitType==2 )  // Big cubit
+      {
+      return VERTICES_BIG;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 3;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float[][] getPositions()
diff --git a/src/main/java/org/distorted/objects/TwistyBandagedFused.java b/src/main/java/org/distorted/objects/TwistyBandagedFused.java
index e034662e..cb9c0ebd 100644
--- a/src/main/java/org/distorted/objects/TwistyBandagedFused.java
+++ b/src/main/java/org/distorted/objects/TwistyBandagedFused.java
@@ -64,6 +64,33 @@ class TwistyBandagedFused extends TwistyBandagedAbstract
        { 0.0f,  0.0f, -1.0f}
       };
 
+  private final double[][] VERTICES_SMALL = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+  private final double[][] VERTICES_BIG = new double[][]
+          {
+              // TODO
+          };
+
+  private final int[][] VERT_INDEXES = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
   private static final int[] QUAT_INDICES = new int[] { 0 };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -74,6 +101,36 @@ class TwistyBandagedFused extends TwistyBandagedAbstract
     super(size, quat, texture, mesh, effects, moves, ObjectList.BAN1, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+     if( cubitType==0 )  // Small cubit
+      {
+      return VERTICES_SMALL;
+      }
+    if( cubitType==1 )  // Big cubit
+      {
+      return VERTICES_BIG;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float[][] getPositions()
diff --git a/src/main/java/org/distorted/objects/TwistyCube.java b/src/main/java/org/distorted/objects/TwistyCube.java
index ab20c361..08f94a25 100644
--- a/src/main/java/org/distorted/objects/TwistyCube.java
+++ b/src/main/java/org/distorted/objects/TwistyCube.java
@@ -91,6 +91,33 @@ class TwistyCube extends TwistyObject
          new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
          };
 
+  private static final double[][] VERTICES = new double[][]
+          {
+              { 0.5, 0.5, 0.5 },
+              { 0.5, 0.5,-0.5 },
+              { 0.5,-0.5, 0.5 },
+              { 0.5,-0.5,-0.5 },
+              {-0.5, 0.5, 0.5 },
+              {-0.5, 0.5,-0.5 },
+              {-0.5,-0.5, 0.5 },
+              {-0.5,-0.5,-0.5 },
+          };
+
+  private static final int[][] VERT_INDEXES = new int[][]
+          {
+              {2,3,1,0},   // counterclockwise!
+              {7,6,4,5},
+              {4,0,1,5},
+              {7,3,2,6},
+              {6,2,0,4},
+              {3,7,5,1}
+          };
+
+  private static final float[][] STICKERS = new float[][]
+          {
+              { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f }
+          };
+
   private static MeshBase[] mMeshes;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -101,12 +128,36 @@ class TwistyCube extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+    return VERTICES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// in the sense of shape, there's always only 1 cubit type.
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 1;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createCubitMesh(int cubit, int numLayers)
     {
     if( mMeshes==null )
       {
+      FactoryCubit factory = FactoryCubit.getInstance();
+      factory.clear();
       mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
       }
 
@@ -115,7 +166,28 @@ class TwistyCube extends TwistyObject
 
     if( mMeshes[index]==null )
       {
-      mMeshes[index] = FactoryCubit.getInstance().createCubeMesh(index);
+      int extraI, extraV, num;
+
+      switch(numLayers)
+        {
+        case 2 : num = 6; extraI = 2; extraV = 2; break;
+        case 3 : num = 5; extraI = 2; extraV = 2; break;
+        case 4 : num = 5; extraI = 1; extraV = 2; break;
+        default: num = 5; extraI = 1; extraV = 0; break;
+        }
+
+      float[][] bands     = new float[][] { {0.038f,35,0.5f,0.7f,num,extraI,extraV} };
+      int[] bandIndexes   = new int[] { 0,0,0,0,0,0};
+      float[][] corners   = new float[][] { {0.036f,0.12f} };
+      int[] cornerIndexes = new int[] { 0,0,0,0,0,0,0,0 };
+
+      FactoryCubit factory = FactoryCubit.getInstance();
+
+      factory.createNewFaceTransform(VERTICES,VERT_INDEXES);
+      mMeshes[index] = factory.createRoundedSolid(VERTICES, VERT_INDEXES,
+                                                  bands, bandIndexes,
+                                                  corners, cornerIndexes,
+                                                  getNumCubitFaces() );
       }
 
     return mMeshes[index].copy(true);
@@ -125,29 +197,27 @@ class TwistyCube extends TwistyObject
 
   void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
     {
-    float F =  0.5f;
     float R = 0.10f;
     float S = 0.08f;
-    float[] vertices = { -F,-F, +F,-F, +F,+F, -F,+F};
 
     FactorySticker factory = FactorySticker.getInstance();
-    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
+    factory.drawRoundedPolygon(canvas, paint, left, top, STICKERS[0], S, FACE_COLORS[face], R);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  float[][] getCubitPositions(int size)
+  float[][] getCubitPositions(int numLayers)
     {
-    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
+    int numCubits = numLayers>1 ? 6*numLayers*numLayers - 12*numLayers + 8 : 1;
     float[][] tmp = new float[numCubits][];
 
-    float diff = 0.5f*(size-1);
+    float diff = 0.5f*(numLayers-1);
     int currentPosition = 0;
 
-    for(int x = 0; x<size; x++)
-      for(int y = 0; y<size; y++)
-        for(int z = 0; z<size; z++)
-          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
+    for(int x = 0; x<numLayers; x++)
+      for(int y = 0; y<numLayers; y++)
+        for(int z = 0; z<numLayers; z++)
+          if( x==0 || x==numLayers-1 || y==0 || y==numLayers-1 || z==0 || z==numLayers-1 )
             {
             tmp[currentPosition++] = new float[] {x-diff,y-diff,z-diff};
             }
@@ -194,7 +264,7 @@ class TwistyCube extends TwistyObject
 
   int getNumStickerTypes(int numLayers)
     {
-    return 1;
+    return STICKERS.length;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objects/TwistyDiamond.java b/src/main/java/org/distorted/objects/TwistyDiamond.java
index 021865c2..416216eb 100644
--- a/src/main/java/org/distorted/objects/TwistyDiamond.java
+++ b/src/main/java/org/distorted/objects/TwistyDiamond.java
@@ -94,6 +94,44 @@ public class TwistyDiamond extends TwistyObject
 
   private static final int[] mTetraToFaceMap = new int[] {1,2,3,0,5,6,7,4};
 
+  private final double[][] VERTICES_TETRA = new double[][]
+          {
+             {-0.5, SQ2/4, 0.0},
+             { 0.5, SQ2/4, 0.0},
+             { 0.0,-SQ2/4, 0.5},
+             { 0.0,-SQ2/4,-0.5}
+          };
+
+  private final int[][] VERT_INDEXES_TETRA = new int[][]
+          {
+             {2,1,0},   // counterclockwise!
+             {2,3,1},
+             {3,2,0},
+             {3,0,1}
+          };
+
+  private final double[][] VERTICES_OCTA = new double[][]
+          {
+             { 0.5,   0.0, 0.5},
+             { 0.5,   0.0,-0.5},
+             {-0.5,   0.0,-0.5},
+             {-0.5,   0.0, 0.5},
+             { 0.0, SQ2/2, 0.0},
+             { 0.0,-SQ2/2, 0.0}
+          };
+
+  private final int[][] VERT_INDEXES_OCTA = new int[][]
+          {
+             {3,0,4},   // counterclockwise!
+             {0,1,4},
+             {1,2,4},
+             {2,3,4},
+             {5,0,3},
+             {5,1,0},
+             {5,2,1},
+             {5,3,2}
+          };
+
   private static MeshBase mOctaMesh, mTetraMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -104,6 +142,45 @@ public class TwistyDiamond extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.DIAM, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+    if( cubitType==0 )  // Tetrahedron
+      {
+      return VERTICES_TETRA;
+      }
+    if( cubitType==1 )  // Octahedron
+      {
+      return VERTICES_OCTA;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    if( cubitType==0 )  // Tetrahedron
+      {
+      return VERT_INDEXES_TETRA;
+      }
+    if( cubitType==1 )  // Octahedron
+      {
+      return VERT_INDEXES_OCTA;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/TwistyDino.java b/src/main/java/org/distorted/objects/TwistyDino.java
index bf3cf4bc..4d2c0216 100644
--- a/src/main/java/org/distorted/objects/TwistyDino.java
+++ b/src/main/java/org/distorted/objects/TwistyDino.java
@@ -88,6 +88,22 @@ public abstract class TwistyDino extends TwistyObject
              {-1.5f, 0.0f,-1.5f }
          };
 
+  private final double[][] VERTICES = new double[][]
+          {
+             {-0.5, 0.0, 0.0},
+             { 0.5, 0.0, 0.0},
+             { 0.0,-0.5, 0.0},
+             { 0.0, 0.0,-0.5}
+          };
+
+  private final int[][] VERT_INDEXES = new int[][]
+          {
+             {2,1,0},   // counterclockwise!
+             {2,3,1},
+             {3,2,0},
+             {3,0,1}
+          };
+
   private static MeshBase mMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -98,6 +114,27 @@ public abstract class TwistyDino extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, obj, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+    return VERTICES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return VERT_INDEXES;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 1;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int mulQuat(int q1, int q2)
diff --git a/src/main/java/org/distorted/objects/TwistyHelicopter.java b/src/main/java/org/distorted/objects/TwistyHelicopter.java
index 408393c5..7f2b2cbc 100644
--- a/src/main/java/org/distorted/objects/TwistyHelicopter.java
+++ b/src/main/java/org/distorted/objects/TwistyHelicopter.java
@@ -185,6 +185,26 @@ public class TwistyHelicopter extends TwistyObject
   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 final double[][] VERTICES_CORNER = new double[][]
+          {
+            // TODO
+          };
+
+  private final int[][] VERT_INDEXES_CORNER = new int[][]
+          {
+            // TODO
+          };
+
+  private final double[][] VERTICES_FACE = new double[][]
+          {
+            // TODO
+          };
+
+  private final int[][] VERT_INDEXES_FACE = new int[][]
+          {
+            // TODO
+          };
+
   private static MeshBase mCornerMesh, mFaceMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -195,6 +215,45 @@ public class TwistyHelicopter extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.HELI, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+     if( cubitType==0 )  // Corner
+      {
+      return VERTICES_CORNER;
+      }
+    if( cubitType==1 )  // Face
+      {
+      return VERTICES_FACE;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    if( cubitType==0 )  // Corner
+      {
+      return VERT_INDEXES_CORNER;
+      }
+    if( cubitType==1 )  // Face
+      {
+      return VERT_INDEXES_FACE;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 1;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/TwistyIvy.java b/src/main/java/org/distorted/objects/TwistyIvy.java
index 146aaa60..8807914b 100644
--- a/src/main/java/org/distorted/objects/TwistyIvy.java
+++ b/src/main/java/org/distorted/objects/TwistyIvy.java
@@ -102,6 +102,29 @@ public class TwistyIvy extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.IVY, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// don't use this
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// don't use this
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/TwistyKilominx.java b/src/main/java/org/distorted/objects/TwistyKilominx.java
index 34befa71..10ac8363 100644
--- a/src/main/java/org/distorted/objects/TwistyKilominx.java
+++ b/src/main/java/org/distorted/objects/TwistyKilominx.java
@@ -80,6 +80,30 @@ public class TwistyKilominx extends TwistyMinx
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.KILO, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 3 --> 1, 5 --> 4, 7 --> 5, 9 --> 6
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return numLayers==3 ? 1 : 2 + numLayers/2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int numCubitsPerCorner(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyMegaminx.java b/src/main/java/org/distorted/objects/TwistyMegaminx.java
index 66372391..e741082a 100644
--- a/src/main/java/org/distorted/objects/TwistyMegaminx.java
+++ b/src/main/java/org/distorted/objects/TwistyMegaminx.java
@@ -62,6 +62,30 @@ public class TwistyMegaminx extends TwistyMinx
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.MEGA, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 3 --> 3, 5 --> 4, 7 --> 5, 9 --> 6
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2 + numLayers/2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int numCubitsPerCorner(int numLayers)
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index cb9b2e10..1d353ae6 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -27,6 +27,7 @@ import android.graphics.Paint;
 
 import com.google.firebase.crashlytics.FirebaseCrashlytics;
 
+import org.distorted.helpers.FactoryCubit;
 import org.distorted.library.effect.Effect;
 import org.distorted.library.effect.MatrixEffectMove;
 import org.distorted.library.effect.MatrixEffectQuaternion;
@@ -80,7 +81,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;
@@ -504,6 +505,27 @@ public abstract class TwistyObject extends DistortedNode
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// create StickerCoord and FaceTransform data structures
+
+  void createFaceDataStructures()
+    {
+    int numCubitTypes = getNumCubitTypes(mNumLayers);
+    FactoryCubit factory = FactoryCubit.getInstance();
+    factory.clear();
+
+    for(int cubit=0; cubit<numCubitTypes; cubit++)
+      {
+      double[][] vertices = getVertices(cubit);
+      int[][] vertIndices = getVertIndexes(cubit);
+
+      factory.createNewFaceTransform(vertices,vertIndices);
+      factory.printFaceTransform();
+      }
+
+    factory.printStickerCoords();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
 
@@ -908,6 +930,9 @@ public abstract class TwistyObject extends DistortedNode
   abstract float[] getRowChances(int numLayers);
   abstract float[] getCuts(int numLayers);
   abstract boolean shouldResetTextureMaps();
+  abstract double[][] getVertices(int cubitType);
+  abstract int[][] getVertIndexes(int cubitType);
+  abstract int getNumCubitTypes(int numLayers);
 
   public abstract boolean isSolved();
   public abstract Static3D[] getRotationAxis();
diff --git a/src/main/java/org/distorted/objects/TwistyPyraminx.java b/src/main/java/org/distorted/objects/TwistyPyraminx.java
index f0bc098d..67e846f1 100644
--- a/src/main/java/org/distorted/objects/TwistyPyraminx.java
+++ b/src/main/java/org/distorted/objects/TwistyPyraminx.java
@@ -70,6 +70,44 @@ public class TwistyPyraminx extends TwistyObject
            new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
          };
 
+  private final double[][] VERTICES_TETRA = new double[][]
+          {
+             {-0.5, SQ2/4, 0.0},
+             { 0.5, SQ2/4, 0.0},
+             { 0.0,-SQ2/4, 0.5},
+             { 0.0,-SQ2/4,-0.5}
+          };
+
+  private final int[][] VERT_INDEXES_TETRA = new int[][]
+          {
+             {2,1,0},   // counterclockwise!
+             {2,3,1},
+             {3,2,0},
+             {3,0,1}
+          };
+
+  private final double[][] VERTICES_OCTA = new double[][]
+          {
+             { 0.5,   0.0, 0.5},
+             { 0.5,   0.0,-0.5},
+             {-0.5,   0.0,-0.5},
+             {-0.5,   0.0, 0.5},
+             { 0.0, SQ2/2, 0.0},
+             { 0.0,-SQ2/2, 0.0}
+          };
+
+  private final int[][] VERT_INDEXES_OCTA = new int[][]
+          {
+             {3,0,4},   // counterclockwise!
+             {0,1,4},
+             {1,2,4},
+             {2,3,4},
+             {5,0,3},
+             {5,1,0},
+             {5,2,1},
+             {5,3,2}
+          };
+
   private static MeshBase mOctaMesh, mTetraMesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -80,6 +118,45 @@ public class TwistyPyraminx extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  double[][] getVertices(int cubitType)
+    {
+    if( cubitType==0 )  // Tetrahedron
+      {
+      return VERTICES_TETRA;
+      }
+    if( cubitType==1 )  // Octahedron
+      {
+      return VERTICES_OCTA;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    if( cubitType==0 )  // Tetrahedron
+      {
+      return VERT_INDEXES_TETRA;
+      }
+    if( cubitType==1 )  // Octahedron
+      {
+      return VERT_INDEXES_OCTA;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void addTetrahedralLattice(int size, int index, float[][] pos)
diff --git a/src/main/java/org/distorted/objects/TwistyRedi.java b/src/main/java/org/distorted/objects/TwistyRedi.java
index a256174f..a8f3cf4c 100644
--- a/src/main/java/org/distorted/objects/TwistyRedi.java
+++ b/src/main/java/org/distorted/objects/TwistyRedi.java
@@ -143,6 +143,29 @@ public class TwistyRedi extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.REDI, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/TwistyRex.java b/src/main/java/org/distorted/objects/TwistyRex.java
index 0e84e025..ee3a9921 100644
--- a/src/main/java/org/distorted/objects/TwistyRex.java
+++ b/src/main/java/org/distorted/objects/TwistyRex.java
@@ -137,6 +137,29 @@ public class TwistyRex extends TwistyObject
     super(size, size, quat, texture, mesh, effects, moves, ObjectList.REX, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// don't use this
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// don't use this
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return 2;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/TwistySkewb.java b/src/main/java/org/distorted/objects/TwistySkewb.java
index 9fef5f5c..78e79866 100644
--- a/src/main/java/org/distorted/objects/TwistySkewb.java
+++ b/src/main/java/org/distorted/objects/TwistySkewb.java
@@ -127,6 +127,29 @@ public class TwistySkewb extends TwistyObject
     super(size, 2*size-2, quat, texture, mesh, effects, moves, ObjectList.SKEW, res, scrWidth);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  double[][] getVertices(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  int[][] getVertIndexes(int cubitType)
+    {
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitTypes(int numLayers)
+    {
+    return numLayers==2 ? 2 : 3;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private int getNumCorners()
