commit 33647db968c1e79e36df9e87d56357d062668a5a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Apr 8 22:14:39 2021 +0200

    Face cubit creation: compiles and runs now (but results are incorrect)

diff --git a/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java b/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java
index 9d5c481..46a20e3 100644
--- a/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java
+++ b/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java
@@ -22,7 +22,7 @@ package org.distorted.examples.meshfile;
 import org.distorted.library.effect.VertexEffect;
 import org.distorted.library.effect.VertexEffectDeform;
 import org.distorted.library.effect.VertexEffectMove;
-import org.distorted.library.effect.VertexEffectRotate;
+import org.distorted.library.effect.VertexEffectQuaternion;
 import org.distorted.library.effect.VertexEffectScale;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.mesh.MeshJoined;
@@ -31,37 +31,36 @@ import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import java.util.ArrayList;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class FactoryCubit
   {
-  private static final float SQ2 = (float)Math.sqrt(2);
-  private static final float SQ3 = (float)Math.sqrt(3);
-  private static final float SQ5 = (float)Math.sqrt(5);
-  private static final float SQ6 = (float)Math.sqrt(6);
-
-  private static final float IVY_D = 0.003f;
-  private static final int   IVY_N = 8;
-  private static final float IVY_C = 0.59f;
-  private static final float IVY_M = 0.35f;
-  private static final float REX_D = 0.2f;
-  private static final int   REX_N = 5;
-  private static final float MEGA_D = 0.04f;
-
-  static final float SIN18    = (SQ5-1)/4;
-  static final float COS18    = (float)(0.25f*Math.sqrt(10.0f+2.0f*SQ5));
-  static final float SIN54    = (SQ5+1)/4;
-  static final float COS54    = (float)(Math.sqrt(10-2*SQ5)/4);
-  static final float COS_HALFD= (float)(Math.sqrt(0.5f-0.1f*SQ5)); // cos(half the dihedral angle)
-  static final float SIN_HALFD= (float)(Math.sqrt(0.5f+0.1f*SQ5)); // sin(half the dihedral angle)
-
-  static final float DIHEDRAL1= (float)(Math.acos(-SQ5/5)*180/Math.PI);
-  static final float DIHEDRAL2= (float)((180/Math.PI)*Math.asin((2*SIN54*SIN54-1)/COS54) - 90);
+  static final float SQ5   = (float)Math.sqrt(5);
+  static final float SIN18 = (SQ5-1)/4;
+  static final float COS18 = (float)(0.25f*Math.sqrt(10.0f+2.0f*SQ5));
+
+  private static final float[] mBuffer = new float[3];
+  private static final float[] mQuat1  = new float[4];
+  private static final float[] mQuat2  = new float[4];
+  private static final float[] mQuat3  = new float[4];
 
   private static final Static1D RADIUS = new Static1D(1);
 
   private static FactoryCubit mThis;
 
+  private static class FaceInfo
+    {
+    float[] vertices;
+    float vx,vy,vz;
+    float scale;
+    float qx,qy,qz,qw;
+    boolean flip;
+    }
+
+  private static final ArrayList<FaceInfo> mFaceInfo = new ArrayList<>();
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private FactoryCubit()
@@ -207,2001 +206,430 @@ class FactoryCubit
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Compute (rx,ry) - coords of a point which is the result of rotation by angle 'radians' of the point
-// (px,py) along axis Z. Center of rotation: (cx,cy). Rotation is counterclockwise!
-// Write (rx,ry) to array[index] and array[index+1].
-
-  private void writeVertex( float cx, float cy, float px, float py, float radians, float[] array, int index)
-    {
-    float vx = px-cx;
-    float vy = py-cy;
-
-    float sinA = (float)Math.sin(radians);
-    float cosA = (float)Math.cos(radians);
-
-    float rvx = vx*cosA - vy*sinA;
-    float rvy = vx*sinA + vy*cosA;
-
-    array[index  ] = rvx + cx;
-    array[index+1] = rvy + cy;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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()
-    {
-    MeshBase[] meshes = new MeshBase[6];
-
-    float E = 0.5f;
-    float F = SQ2/2;
-    float G = SQ6/16;
-    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
-    float[] bands0 = computeBands(0.028f,35,E/3,0.7f,7);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
-    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);
-
-    float[] vertices1 = { -F/2,-2*G, F/2,-2*G, 3*F/8,-G, 1*F/8,G, 0,2*G };
-    float[] bands1 = computeBands(0,0,1,0,3);
-
-    meshes[3] = new MeshPolygon(vertices1,bands1,1,5);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesSkewbFace()
-    {
-    MeshBase[] meshes = new MeshBase[5];
-
-    float E = SQ2/4;
-    float[] vertices0 = { -E,-E, +E,-E, +E,+E, -E,+E };
-    float[] bands0 = computeBands(0.051f,35,E/2,0.9f,7);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
-    meshes[0].setEffectAssociation(0,1,0);
-
-    float[] vertices1 = { -E,-SQ3*E, -E*0.7f,-SQ3*E, +E*0.7f,-SQ3*E, +E,-SQ3*E, 0,0 };
-    float[] bands1 = computeBands(0,0,1,0,3);
-
-    meshes[1] = new MeshPolygon(vertices1,bands1,0,0);
-    meshes[1].setEffectAssociation(0,2,0);
-    meshes[2] = meshes[1].copy(true);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[1].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[1].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesOcta()
-    {
-    MeshBase[] meshes = new MeshPolygon[8];
-
-    float E = 0.75f;
-    float F = 0.5f;
-    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
-    float[] bands = computeBands(0.05f,35,F,0.8f,6);
-
-    meshes[0] = new MeshPolygon(vertices, bands, 2,2);
-    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);
-    meshes[6] = meshes[0].copy(true);
-    meshes[6].setEffectAssociation(0,64,0);
-    meshes[7] = meshes[0].copy(true);
-    meshes[7].setEffectAssociation(0,128,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesTetra()
-    {
-    MeshBase[] meshes = new MeshBase[4];
-
-    float E = 0.75f;
-    float F = 0.5f;
-    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
-    float[] bands = computeBands(0.05f,35,F,0.8f,6);
-
-    meshes[0] = new MeshPolygon(vertices, bands, 2,2);
-    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);
-
-    return new MeshJoined(meshes);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createFacesDino()
+  private boolean areColinear(float[][] vertices, int index1, int index2, int index3)
     {
-    MeshBase[] meshes = new MeshPolygon[4];
+    float x1 = vertices[index1][0];
+    float y1 = vertices[index1][1];
+    float z1 = vertices[index1][2];
+    float x2 = vertices[index2][0];
+    float y2 = vertices[index2][1];
+    float z2 = vertices[index2][2];
+    float x3 = vertices[index3][0];
+    float y3 = vertices[index3][1];
+    float z3 = vertices[index3][2];
 
-    float E = 0.5f*SQ2;
-    float F = 0.5f;
-    float[] vertices0 = { -F,F/3, 0,-2*F/3, +F,F/3 };
-    float[] bands0 = computeBands(0.028f,30,F/3,0.8f,7);
+    float v1x = x2-x1;
+    float v1y = y2-y1;
+    float v1z = z2-z1;
+    float v2x = x3-x1;
+    float v2y = y3-y1;
+    float v2z = z3-z1;
 
-    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 5);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
+    float A = (float)Math.sqrt( (v1x*v1x+v1y*v1y+v1z*v1z) / (v2x*v2x+v2y*v2y+v2z*v2z) );
 
-    float[] vertices1 = { -E/2,-E*(SQ3/6), E/2,-E*(SQ3/6), 0,E*(SQ3/3) };
-    float[] bands1 = computeBands(0.02f,45,F/3,0.2f,3);
+//android.util.Log.e("D", "("+x1+","+y1+","+z1+") , ("+x2+","+y2+","+z2+") , ("+x3+","+y3+","+z3+")" );
 
-    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
+//boolean result = (v1x==A*v2x && v1y==A*v2y && v1z==A*v2z);
 
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+//android.util.Log.e("D", "are those colinear? : "+result);
 
-  MeshBase createFacesHelicopterCorner()
-    {
-    MeshBase[] meshes = new MeshBase[6];
-
-    float E = 0.5f;
-    float F = SQ2/4;
-    float G = 1.0f/12;
-    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
-    float[] bands0 = computeBands(0.028f,35,E/4,0.7f,7);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
-    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);
-
-    float[] vertices1 = { -F,-G, 0,-G, +F,-G, 0,2*G };
-    float[] bands1 = computeBands(0.00f,0,0,0.0f,3);
-    meshes[3] = new MeshPolygon(vertices1,bands1,1,5);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
+    return (v1x==A*v2x && v1y==A*v2y && v1z==A*v2z);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createFacesHelicopterFace()
+  private void computeNormalVector(float[][] vertices, int index1, int index2, int index3)
     {
-    MeshBase[] meshes = new MeshBase[4];
-
-    float E = 0.5f;
-    float F = SQ2/4;
-    float G = 1.0f/12;
-    float[] vertices0 = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
-    float[] bands0 = computeBands(0.028f,35,E/4,0.7f,7);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 3, 3);
-    meshes[0].setEffectAssociation(0,1,0);
-
-    float[] vertices1 = { -F,-G, +F,-G, 0,2*G};
-    float[] bands1 = computeBands(0.01f,45,F,0.0f,3);
+    float x1 = vertices[index1][0];
+    float y1 = vertices[index1][1];
+    float z1 = vertices[index1][2];
+    float x2 = vertices[index2][0];
+    float y2 = vertices[index2][1];
+    float z2 = vertices[index2][2];
+    float x3 = vertices[index3][0];
+    float y3 = vertices[index3][1];
+    float z3 = vertices[index3][2];
 
-    meshes[1] = new MeshPolygon(vertices1, bands1, 1, 3);
-    meshes[1].setEffectAssociation(0,2,0);
+    float v1x = x2-x1;
+    float v1y = y2-y1;
+    float v1z = z2-z1;
+    float v2x = x3-x1;
+    float v2y = y3-y1;
+    float v2z = z3-z1;
 
-    float[] vertices2 = { -E/2,-F/3, +E/2,-F/3, 0,2*F/3};
-
-    meshes[2] = new MeshPolygon(vertices2, bands1, 1, 3);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-
-    return new MeshJoined(meshes);
+    mBuffer[0] = v1y*v2z - v2y*v1z;
+    mBuffer[1] = v1z*v2x - v2z*v1x;
+    mBuffer[2] = v1x*v2y - v2x*v1y;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// return quat1*quat2
 
-  MeshBase createFacesRediEdge()
+  private static void quatMultiply( float[] quat1, float[] quat2, float[] result )
     {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float F = 0.25f;
-    float[] vertices0 = { -F,+F, -F,-F, 0, -2*F, +F,-F, +F,+F };
-    float[] bands0 = computeBands(0.038f,35,F,0.7f,7);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 2);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
-
-    float[] bands1 = computeBands(0.02f,35,F/2,0.2f,3);
-    float[] vertices1 = { -F/2, +F/2, -F/2, -1.5f*F, 1.5f*F, +F/2 };
-
-    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
+    float qx = quat1[0];
+    float qy = quat1[1];
+    float qz = quat1[2];
+    float qw = quat1[3];
 
-    float X = 0.25f*SQ2;
-    float Y = SQ6/16;
-    float[] vertices2 = { -X, Y, -1.5f*X, -Y, +1.5f*X, -Y, +X, Y };
+    float rx = quat2[0];
+    float ry = quat2[1];
+    float rz = quat2[2];
+    float rw = quat2[3];
 
-    meshes[4] = new MeshPolygon(vertices2, bands1, 1, 1);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[4].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
+    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;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createFacesRediCorner()
+  private void fitInSquare(FaceInfo info, float[][] vert3D)
     {
-    MeshBase[] meshes = new MeshBase[6];
-
-    float E = 0.5f;
-    float[] vertices0 = { -E,-E, +E,-E, +E,+E, -E,+E };
-    float[] bands0 = computeBands(0.06f,35,E,0.7f,6);
-
-    meshes[0] = new MeshPolygon(vertices0,bands0,2,2);
-    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);
-
-    float F = 0.5f;
-    float X = 0.5f;
-    float G = 0.72f;
-    float[] vertices1 = { -E,+F, -E+X,0, -E,-F, -E*G,-F, +E*G,-F, +E,-F, +E-X,0, +E,+F, +E*G,+F, -E*G,+F };
-    float[] bands1 = computeBands(0.0f,0,1.0f,0.0f,2);
-
-    meshes[3] = new MeshPolygon(vertices1,bands1,0,0);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    float minX = Float.MAX_VALUE;
+    float maxX =-Float.MAX_VALUE;
+    float minY = Float.MAX_VALUE;
+    float maxY =-Float.MAX_VALUE;
 
-  MeshBase createFacesIvyCorner()
-    {
-    MeshBase[] meshes = new MeshBase[6];
+    for (float[] vert : vert3D)
+      {
+      float x = vert[0];
+      float y = vert[1];
 
-    final float angle = (float)Math.PI/(2*IVY_N);
-    final float CORR  = 1.0f - 2*IVY_D;
-    final float DIST  = -0.5f*CORR + IVY_D;
-    float[] vertices  = new float[2*(IVY_N+1)+6];
+      if (x > maxX) maxX = x;
+      if (x < minX) minX = x;
+      if (y > maxY) maxY = y;
+      if (y < minY) minY = y;
+      }
 
-    vertices[0] = (0.5f-IVY_M) * IVY_C;
-    vertices[1] = (DIST-IVY_M) * IVY_C;
-    vertices[2] = (0.5f-IVY_M) * IVY_C;
-    vertices[3] = (0.5f-IVY_M) * IVY_C;
-    vertices[4] = (DIST-IVY_M) * IVY_C;
-    vertices[5] = (0.5f-IVY_M) * IVY_C;
+    info.scale = Math.max(maxX-minX,maxY-minY);
+    int len = vert3D.length;
+    info.vertices = new float[2*len];
 
-    for(int i=0; i<=IVY_N; i++)
+    for( int vertex=0; vertex<len; vertex++ )
       {
-      float ang = (IVY_N-i)*angle;
-      float sin = (float)Math.sin(ang);
-      float cos = (float)Math.cos(ang);
-
-      vertices[2*i+6] = (CORR*(cos-0.5f)-IVY_M)*IVY_C;
-      vertices[2*i+7] = (CORR*(sin-0.5f)-IVY_M)*IVY_C;
+      info.vertices[2*vertex  ] = vert3D[vertex][0] / info.scale;
+      info.vertices[2*vertex+1] = vert3D[vertex][1] / info.scale;
       }
 
-    float[] bands0 = computeBands(+0.012f,20,0.2f,0.5f,7);
-    float[] bands1 = computeBands(-0.100f,20,0.2f,0.0f,2);
-
-    meshes[0] = new MeshPolygon(vertices,bands0,1,2);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
-    meshes[2] = meshes[0].copy(true);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = new MeshPolygon(vertices,bands1,1,2);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
+    info.flip = false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createFacesIvyFace()
+  private void constructNew(FaceInfo info, final float[][] vert3D)
     {
-    MeshBase[] meshes = new MeshBase[2];
-
-    final float angle = (float)Math.PI/(2*IVY_N);
-    final float CORR  = 1.0f - IVY_D*SQ2;
-    float[] vertices = new float[4*IVY_N];
+    // compute center of gravity
+    info.vx = 0.0f;
+    info.vy = 0.0f;
+    info.vz = 0.0f;
+    int len = vert3D.length;
 
-    for(int i=0; i<IVY_N; i++)
+    for (float[] vert : vert3D)
       {
-      float sin = (float)Math.sin(i*angle);
-      float cos = (float)Math.cos(i*angle);
-
-      vertices[2*i          ] = CORR*(0.5f-cos);
-      vertices[2*i+1        ] = CORR*(0.5f-sin);
-      vertices[2*i  +2*IVY_N] = CORR*(cos-0.5f);
-      vertices[2*i+1+2*IVY_N] = CORR*(sin-0.5f);
+      info.vx += vert[0];
+      info.vy += vert[1];
+      info.vz += vert[2];
       }
 
-    float[] bands0 = computeBands(+0.05f,35,0.5f,0.5f,5);
-    float[] bands1 = computeBands(-0.10f,45,0.5f,0.0f,2);
-
-    meshes[0] = new MeshPolygon(vertices,bands0,0,0);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = new MeshPolygon(vertices,bands1,0,0);
-    meshes[1].setEffectAssociation(0,2,0);
+    info.vx /= len;
+    info.vy /= len;
+    info.vz /= len;
 
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesRexCorner()
-    {
-    MeshBase[] meshes = new MeshBase[2];
-
-    final float angle = (float)Math.PI/(6*REX_N);
-    float[] vertices = new float[6*REX_N];
-    final float D = 0.5f - REX_D;
-    final float F = D*SQ2*(SQ3-1);
-    final float B = 2.5f;
-
-    final float V1x = -F*0.5f;
-    final float V1y = -F*SQ3/6;
-    final float V2x = -V1x;
-    final float V2y = V1y;
-    final float V3x = 0.0f;
-    final float V3y = -2*V1y;
-
-    final float C1x = 0.0f;
-    final float C1y = -F*(1+2*SQ3/3);
-    final float C2x = B*V1x;
-    final float C2y = B*V1y;
-    final float C3x = B*V2x;
-    final float C3y = B*V2y;
-
-    for(int i=0; i<REX_N; i++)
+    // move all vertices so that their center of gravity is at (0,0,0)
+    for (int i=0; i<len; i++)
       {
-      writeVertex(C1x,C1y,V1x,V1y,-i*angle, vertices, 2*i          );
-      writeVertex(C2x,C2y,V2x,V2y, i*angle, vertices, 2*i + 2*REX_N);
-      writeVertex(C3x,C3y,V3x,V3y, i*angle, vertices, 2*i + 4*REX_N);
+      vert3D[i][0] -= info.vx;
+      vert3D[i][1] -= info.vy;
+      vert3D[i][2] -= info.vz;
       }
 
-    float[] bands0 = computeBands(+0.02f,10,0.5f,0.5f,5);
-    float[] bands1 = computeBands(-0.00f,45,0.5f,0.0f,2);
-
-    meshes[0] = new MeshPolygon(vertices,bands0,1,1);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = new MeshPolygon(vertices,bands1,0,0);
-    meshes[1].setEffectAssociation(0,2,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    // find 3 non-colinear vertices
+    int foundIndex = -1;
 
-  MeshBase createFacesRexFace()
-    {
-    MeshBase[] meshes = new MeshBase[2];
-
-    final float angle = (float)Math.PI/(6*REX_N);
-    float[] vertices = new float[8*REX_N];
-    final float D = 0.5f - REX_D;
-    final float F = D*(SQ3-1);
-
-    final float V1x = 0.0f;
-    final float V1y = +F;
-    final float V2x = -F;
-    final float V2y = 0.0f;
-    final float V3x = 0.0f;
-    final float V3y = -F;
-    final float V4x = +F;
-    final float V4y = 0.0f;
-
-    final float C1x = +D;
-    final float C1y = -D;
-    final float C2x = +D;
-    final float C2y = +D;
-    final float C3x = -D;
-    final float C3y = +D;
-    final float C4x = -D;
-    final float C4y = -D;
-
-    for(int i=0; i<REX_N; i++)
+    for(int vertex=2; vertex<len; vertex++)
       {
-      writeVertex(C1x,C1y,V1x,V1y, i*angle, vertices, 2*i          );
-      writeVertex(C2x,C2y,V2x,V2y, i*angle, vertices, 2*i + 2*REX_N);
-      writeVertex(C3x,C3y,V3x,V3y, i*angle, vertices, 2*i + 4*REX_N);
-      writeVertex(C4x,C4y,V4x,V4y, i*angle, vertices, 2*i + 6*REX_N);
+      if( !areColinear(vert3D,0,1,vertex) )
+        {
+        foundIndex = vertex;
+        break;
+        }
       }
 
-    float[] bands0 = computeBands(+0.02f,10,0.5f,0.5f,5);
-    float[] bands1 = computeBands(-0.00f,45,0.5f,0.0f,2);
-
-    meshes[0] = new MeshPolygon(vertices,bands0,0,0);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = new MeshPolygon(vertices,bands1,0,0);
-    meshes[1].setEffectAssociation(0,2,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesRexEdge()
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float E = 0.5f - REX_D;
-    float F = 0.5f;
-    float[] vertices0 = { -F,E/3, 0,-2*E/3, +F,E/3 };
-    float[] bands0 = computeBands(0.03f,27,F/3,0.8f,5);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 2, 3);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
-
-    float G = (float)Math.sqrt(E*E+F*F);
-    float[] vertices1 = { -2*G/3, -E/3, G/3, -E/3, G/3, 2*E/3 };
-    float[] bands1 = computeBands(0.00f,45,G/3,0.2f,3);
-
-    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 2);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = meshes[2].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[2].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesKilominxCorner()
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float X1= (SQ5+1)/8;
-    float Y1= (float)(Math.sqrt(2+0.4f*SQ5)/4);
-    float Y2= Y1 - (float)(Math.sqrt(10-2*SQ5)/8);
-    float H = 0.5f* SIN54 / COS54;
-    float X2= H*SIN_HALFD;
-    float Y3= H/(2*COS_HALFD);
-    float Y4= H*(1/(2*COS_HALFD) - COS_HALFD);
-
-    float[] vertices0 = { -X1, Y2, 0, -Y1, X1, Y2, 0, Y1 };
-    float[] bands0 = computeBands(0.03f,39,0.3f,0.2f,5);
-    float[] vertices1 = { -X2, Y4, 0, -Y3, X2, Y4, 0, Y3 };
-    float[] bands1 = computeBands(0.00f,27,0.25f,0.5f,5);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 1, 1);
-    meshes[0].setEffectAssociation(0, 1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0, 2,0);
-    meshes[2] = meshes[0].copy(true);
-    meshes[2].setEffectAssociation(0, 4,0);
-    meshes[3] = new MeshPolygon(vertices1, bands1, 1, 1);
-    meshes[3].setEffectAssociation(0, 8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesMinxCorner(int numLayers)
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float Y = COS54/(2*SIN54);
-
-    float[] vertices0 = { -0.5f, 0.0f, 0.0f, -Y, 0.5f, 0.0f, 0.0f, Y };
-
-    int numBands0 = numLayers==3 ? 5 : 3;
-    int numBands1 = numLayers==3 ? 2 : 2;
-    float h       = numLayers==3 ? 0.04f : 0.03f;
-    int   e       = numLayers==3 ? 4 : 1;
-
-    float[] bands0 = computeBands(h    ,34,0.3f,0.2f, numBands0);
-    float[] bands1 = computeBands(0.00f,34,0.3f,0.2f, numBands1);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 1, 1);
-    meshes[0].setEffectAssociation(0, 1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0, 2,0);
-    meshes[2] = meshes[0].copy(true);
-    meshes[2].setEffectAssociation(0, 4,0);
-    meshes[3] = new MeshPolygon(vertices0, bands1, 1, e);
-    meshes[3].setEffectAssociation(0, 8,0);
-    meshes[4] = meshes[3].copy(true);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[3].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesKilominxEdge(int numLayers, float width, float height)
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float D = height/COS18;
-    float W = D*SIN18;
-    float X1 = height/2;
-    float Y1 = width/2;
-    float Y2 = (width+W)/2;
-    float X3 = D*SIN54;
-    float Y3 = D*COS54;
-    float X4 = height*SIN_HALFD;
-    float Y4 = height*COS_HALFD;
-
-    float[] vertices0 = { -X1,-Y1, X1, -Y1, X1, Y1+W,-X1, Y1 };
-    float[] vertices1 = { -X1,-Y2, X1, -Y2, X1, Y2+W,-X1, Y2 };
-    float[] vertices2 = { -X3, 0.0f, 0.0f, -Y3, X3, 0.0f, 0.0f, Y3 };
-    float[] vertices3 = { -X4, 0.0f, 0.0f, -Y4, X4, 0.0f, 0.0f, Y4 };
-
-    int numBands0 = numLayers<=5 ? 5 : 3;
-    int numBands1 = numLayers<=5 ? 3 : 2;
-    float h       = numLayers<=5 ? 0.03f : 0.03f;
-
-    float[] bands0 = computeBands(h    ,34,0.2f,0.2f,numBands0);
-    float[] bands1 = computeBands(0.01f,34,0.3f,0.2f,numBands1);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 1, 1);
-    meshes[0].setEffectAssociation(0, 1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0, 2,0);
-    meshes[2] = new MeshPolygon(vertices1, bands1, 0, 0);
-    meshes[2].setEffectAssociation(0, 4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0, 8,0);
-    meshes[4] = new MeshPolygon(vertices2, bands1, 1, 2);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = new MeshPolygon(vertices3, bands1, 1, 2);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createFacesMegaminxEdge(float width, float height)
-    {
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    float D = height/COS18;
-    float W = D*SIN18;
-
-    float Y1 = 0.5f*width;
-    float Y2 = 0.5f*width + W;
-    float Y3 = 0.5f*width + 2*W;
-    float X2 = D*SIN54;
-    float X1 = 0.5f*height;
-    float Y4 = D*COS54;
-
-    float[] vertices0 = { -X1, Y1, -X1, -Y1, X1, -Y2, X1, Y2 };
-    float[] vertices1 = { -X1, Y3, -X1, -Y3, X1, -Y2, X1, Y2 };
-    float[] vertices2 = { -X2, 0.0f, 0.0f, -Y4, X2, 0.0f, 0.0f, Y4 };
-
-    float[] bands0 = computeBands(0.04f,34,  X1,0.2f,5);
-    float[] bands1 = computeBands(0.00f,34,0.3f,0.2f,2);
-
-    meshes[0] = new MeshPolygon(vertices0, bands0, 1, 1);
-    meshes[0].setEffectAssociation(0, 1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0, 2,0);
-    meshes[2] = new MeshPolygon(vertices1, bands1, 1, 4);
-    meshes[2].setEffectAssociation(0, 4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0, 8,0);
-    meshes[4] = new MeshPolygon(vertices2, bands1, 1, 4);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[4].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[] createVertices(int A, int B)
-    {
-    float E = 0.5f / Math.max(A,B);
-    return new float[] { -A*E,-B*E, +A*E,-B*E, +A*E,+B*E, -A*E,+B*E };
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createCuboid(int[] dimensions)
-    {
-    int X = dimensions[0];
-    int Y = dimensions[1];
-    int Z = dimensions[2];
-
-    float[] verticesXY = createVertices(X,Y);
-    float[] verticesXZ = createVertices(X,Z);
-    float[] verticesYZ = createVertices(Z,Y);
-
-    float defHeight = 0.048f;
-
-    float[] bandsX = computeBands( defHeight/X,65,0.25f,0.5f,5);
-    float[] bandsY = computeBands( defHeight/Y,65,0.25f,0.5f,5);
-    float[] bandsZ = computeBands( defHeight/Z,65,0.25f,0.5f,5);
-
-    MeshBase[] meshes = new MeshPolygon[6];
-
-    meshes[0] = new MeshPolygon(verticesYZ,bandsX,1,2);
-    meshes[0].setEffectAssociation(0,1,0);
-    meshes[1] = meshes[0].copy(true);
-    meshes[1].setEffectAssociation(0,2,0);
-    meshes[2] = new MeshPolygon(verticesXZ,bandsY,1,2);
-    meshes[2].setEffectAssociation(0,4,0);
-    meshes[3] = meshes[2].copy(true);
-    meshes[3].setEffectAssociation(0,8,0);
-    meshes[4] = new MeshPolygon(verticesXY,bandsZ,1,2);
-    meshes[4].setEffectAssociation(0,16,0);
-    meshes[5] = meshes[4].copy(true);
-    meshes[5].setEffectAssociation(0,32,0);
-
-
-    return new MeshJoined(meshes);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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()
-    {
-    float E = 0.5f;
-
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static3D axis0  = new Static3D(-SQ2/2,0,SQ2/2);
-    Static3D axis1  = new Static3D(+SQ3/3,+SQ3/3,+SQ3/3);
-    Static1D angle1 = new Static1D(+90);
-    Static1D angle2 = new Static1D(-90);
-    Static1D angle3 = new Static1D(-15);
-    Static1D angle4 = new Static1D((float)((180.0f/Math.PI)*Math.acos(SQ3/3)));
-    Static1D angle5 = new Static1D(120);
-    Static1D angle6 = new Static1D(240);
-    Static3D center1= new Static3D(0,0,0);
-    Static3D center2= new Static3D(-0.5f,-0.5f,-0.5f);
-    Static3D move1  = new Static3D(-E/4,-E/4,0);
-    Static3D move2  = new Static3D(-0.5f+SQ2/4,-0.5f+SQ6/8,-0.5f);
-
-    VertexEffect[] effect = new VertexEffect[10];
-
-    effect[0] = new VertexEffectMove(move1);
-    effect[1] = new VertexEffectScale(new Static3D(1,1,-1));
-    effect[2] = new VertexEffectRotate(angle1,axisX,center1);
-    effect[3] = new VertexEffectRotate(angle2,axisY,center1);
-    effect[4] = new VertexEffectMove(move2);
-    effect[5] = new VertexEffectRotate(angle1,axisX,center2);
-    effect[6] = new VertexEffectRotate(angle3,axisY,center2);
-    effect[7] = new VertexEffectRotate(angle4,axis0,center2);
-    effect[8] = new VertexEffectRotate(angle5,axis1,center2);
-    effect[9] = new VertexEffectRotate(angle6,axis1,center2);
-
-    effect[0].setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect[1].setMeshAssociation( 6,-1);  // meshes 1,2
-    effect[2].setMeshAssociation( 2,-1);  // mesh 1
-    effect[3].setMeshAssociation( 4,-1);  // mesh 2
-    effect[4].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[5].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[6].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[7].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[8].setMeshAssociation(16,-1);  // mesh 4
-    effect[9].setMeshAssociation(32,-1);  // mesh 5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsSkewbFace()
-    {
-    Static3D center = new Static3D(0,0,0);
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisZ  = new Static3D(0,0,1);
-    float angle = -(float)((180.0f/Math.PI)*Math.acos(SQ3/3));
-
-    VertexEffect[] effect = new VertexEffect[6];
-
-    effect[0] = new VertexEffectRotate( new Static1D(angle), axisX, center);
-    effect[1] = new VertexEffectRotate( new Static1D(  135), axisZ, center);
-    effect[2] = new VertexEffectRotate( new Static1D(   45), axisZ, center);
-    effect[3] = new VertexEffectRotate( new Static1D(  -45), axisZ, center);
-    effect[4] = new VertexEffectRotate( new Static1D( -135), axisZ, center);
-    effect[5] = new VertexEffectMove( new Static3D(0,0,-0.5f) );
-
-    effect[0].setMeshAssociation(30,-1);  // meshes 1,2,3,4
-    effect[1].setMeshAssociation( 2,-1);  // mesh 1
-    effect[2].setMeshAssociation( 5,-1);  // meshes 0,2
-    effect[3].setMeshAssociation( 8,-1);  // mesh 3
-    effect[4].setMeshAssociation(16,-1);  // mesh 4
-    effect[5].setMeshAssociation(30,-1);  // meshes 1,2,3,4
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsOcta()
-    {
-    Static1D alpha = new Static1D((float)(-(180/Math.PI)*Math.asin(SQ3/3)));
-    Static1D angle1= new Static1D( 90);
-    Static1D angle2= new Static1D(180);
-    Static1D angle3= new Static1D(270);
-    Static3D move1 = new Static3D(0,SQ2/2-SQ3/3,0);
-    Static3D axisX = new Static3D(1,0,0);
-    Static3D axisY = new Static3D(0,1,0);
-    Static3D cent0 = new Static3D(0,0,0);
-    Static3D cent1 = new Static3D(0,SQ2/2,0);
-    Static3D flipY = new Static3D( 1,-1, 1);
-    Static3D scale = new Static3D( 1, 2*SQ3/3, 1);
-
-    VertexEffect[] effect = new VertexEffect[7];
-
-    effect[0] = new VertexEffectScale(scale);
-    effect[1] = new VertexEffectMove(move1);
-    effect[2] = new VertexEffectRotate(alpha , axisX, cent1);
-    effect[3] = new VertexEffectRotate(angle1, axisY, cent0);
-    effect[4] = new VertexEffectRotate(angle2, axisY, cent0);
-    effect[5] = new VertexEffectRotate(angle3, axisY, cent0);
-    effect[6] = new VertexEffectScale(flipY);
-
-    effect[3].setMeshAssociation ( 34,-1); // apply to meshes 1 & 5
-    effect[4].setMeshAssociation ( 68,-1); // apply to meshes 2 & 6
-    effect[5].setMeshAssociation (136,-1); // apply to meshes 3 & 7
-    effect[6].setMeshAssociation (240,-1); // apply to meshes 4,5,6,7
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsTetra()
-    {
-    Static3D flipZ = new Static3D( 1, 1,-1);
-    Static1D alpha = new Static1D((float)(-(180/Math.PI)*Math.asin(SQ3/3)));
-    Static1D angle1= new Static1D( 90);
-    Static1D angle2= new Static1D(180);
-    Static3D move1 = new Static3D(0,SQ2/4-SQ3/6,0);
-    Static3D axisX = new Static3D(1,0,0);
-    Static3D axisY = new Static3D(0,1,0);
-    Static3D axisZ = new Static3D(0,0,1);
-    Static3D cent0 = new Static3D(0,0,0);
-    Static3D cent1 = new Static3D(0,SQ2/4,0);
-    Static3D scale = new Static3D( 1, 2*SQ3/3, 1);
-
-    VertexEffect[] effect = new VertexEffect[7];
-
-    effect[0] = new VertexEffectScale(scale);
-    effect[1] = new VertexEffectRotate(angle2, axisZ, cent0);
-    effect[2] = new VertexEffectMove(move1);
-    effect[3] = new VertexEffectRotate(alpha , axisX, cent1);
-    effect[4] = new VertexEffectScale(flipZ);
-    effect[5] = new VertexEffectRotate(angle1, axisY, cent0);
-    effect[6] = new VertexEffectRotate(angle2, axisZ, cent0);
-
-    effect[4].setMeshAssociation(10,-1); // meshes 1 & 3
-    effect[5].setMeshAssociation(12,-1); // meshes 2 & 3
-    effect[6].setMeshAssociation(12,-1); // meshes 2 & 3
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsDino()
-    {
-    float E = 0.5f*SQ2;
-    float F = 0.5f;
-    final float ANGLE = (float)((180/Math.PI)*(Math.atan(SQ2)));
-
-    Static1D angle1 = new Static1D(-ANGLE);
-    Static1D angle2 = new Static1D(+ANGLE);
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static3D axisZ  = new Static3D(0,-1,1);
-    Static3D center0= new Static3D(0,0,0);
-    Static3D center1= new Static3D(0,-3*F,0);
-
-    VertexEffect[] effect = new VertexEffect[10];
-
-    effect[0] = new VertexEffectScale ( new Static3D(3,3,3) );
-    effect[1] = new VertexEffectMove  ( new Static3D(0,-F,0) );
-    effect[2] = new VertexEffectRotate( new Static1D(90), axisX, center0 );
-    effect[3] = new VertexEffectScale ( new Static3D(1,-1,1) );
-    effect[4] = new VertexEffectMove  ( new Static3D(3*E/2,E*(SQ3/2)-3*F,0) );
-    effect[5] = new VertexEffectRotate( new Static1D(+90), axisY, center1 );
-    effect[6] = new VertexEffectScale ( new Static3D(-1,1,1) );
-    effect[7] = new VertexEffectRotate( new Static1D( 45), axisX, center1 );
-    effect[8] = new VertexEffectRotate( angle1           , axisZ, center1 );
-    effect[9] = new VertexEffectRotate( angle2           , axisZ, center1 );
-
-    effect[0].setMeshAssociation(15,-1);  // apply to meshes 0,1,2,3
-    effect[1].setMeshAssociation( 3,-1);  // apply to meshes 0,1
-    effect[2].setMeshAssociation( 2,-1);  // apply to mesh 1
-    effect[3].setMeshAssociation( 2,-1);  // apply to mesh 0
-    effect[4].setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect[5].setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect[6].setMeshAssociation( 8,-1);  // apply to mesh 3
-    effect[7].setMeshAssociation(12,-1);  // apply to meshes 2,3
-    effect[8].setMeshAssociation( 4,-1);  // apply to mesh 2
-    effect[9].setMeshAssociation( 8,-1);  // apply to mesh 3
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsHelicopterCorner()
-    {
-    float E = 0.5f;
-
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static3D axis0  = new Static3D(-SQ2/2,0,SQ2/2);
-    Static3D axis1  = new Static3D(+SQ3/3,+SQ3/3,+SQ3/3);
-    Static1D angle1 = new Static1D(+90);
-    Static1D angle2 = new Static1D(-90);
-    Static1D angle3 = new Static1D(-135);
-    Static1D angle4 = new Static1D(90);
-    Static1D angle5 = new Static1D(120);
-    Static1D angle6 = new Static1D(240);
-    Static3D center1= new Static3D(0,0,0);
-    Static3D center2= new Static3D(-0.25f,-0.25f,-0.25f);
-    Static3D move1  = new Static3D(-E/4,-E/4,0);
-    Static3D move2  = new Static3D(-0.25f,(-1.0f/6)-0.25f,-0.25f);
-
-    VertexEffect[] effect = new VertexEffect[10];
-
-    effect[0] = new VertexEffectMove(move1);
-    effect[1] = new VertexEffectScale(new Static3D(1,1,-1));
-    effect[2] = new VertexEffectRotate(angle1,axisX,center1);
-    effect[3] = new VertexEffectRotate(angle2,axisY,center1);
-    effect[4] = new VertexEffectMove(move2);
-    effect[5] = new VertexEffectRotate(angle1,axisX,center2);
-    effect[6] = new VertexEffectRotate(angle3,axisY,center2);
-    effect[7] = new VertexEffectRotate(angle4,axis0,center2);
-    effect[8] = new VertexEffectRotate(angle5,axis1,center2);
-    effect[9] = new VertexEffectRotate(angle6,axis1,center2);
-
-    effect[0].setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect[1].setMeshAssociation( 6,-1);  // meshes 1,2
-    effect[2].setMeshAssociation( 2,-1);  // mesh 1
-    effect[3].setMeshAssociation( 4,-1);  // mesh 2
-    effect[4].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[5].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[6].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[7].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[8].setMeshAssociation(16,-1);  // mesh 4
-    effect[9].setMeshAssociation(32,-1);  // mesh 5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsHelicopterFace()
-    {
-    float E = 0.5f;
-    float F = SQ2/4;
-
-    Static3D move0  = new Static3D(-E/4, -E/4, 0);
-    Static3D move1  = new Static3D(-(SQ2/24)-E/2, -(SQ2/24)-E/2, 0);
-    Static3D move2  = new Static3D(-E/2, F/3, 0);
-    Static3D move3  = new Static3D(+E/2, F/3, 0);
-    Static3D move4  = new Static3D(+E/3,+E/3, 0);
-    Static1D angle1 = new Static1D(135);
-    Static1D angle2 = new Static1D(90);
-    Static1D angle3 = new Static1D(-90);
-    Static1D angle4 = new Static1D(-135);
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static3D axisZ  = new Static3D(0,0,1);
-    Static3D axis1  = new Static3D(1,-1,0);
-    Static3D center = new Static3D(0,0,0);
-    Static3D center1= new Static3D(-E/2,-E/2,0);
-
-    VertexEffect[] effect = new VertexEffect[10];
-
-    effect[0] = new VertexEffectMove(move0);
-    effect[1] = new VertexEffectRotate(angle1, axisZ, center);
-    effect[2] = new VertexEffectMove(move1);
-    effect[3] = new VertexEffectRotate(angle2, axis1, center1);
-    effect[4] = new VertexEffectMove(move2);
-    effect[5] = new VertexEffectMove(move3);
-    effect[6] = new VertexEffectRotate(angle3, axisZ, center);
-    effect[7] = new VertexEffectRotate(angle4, axisX, center);
-    effect[8] = new VertexEffectRotate(angle1, axisY, center);
-    effect[9] = new VertexEffectMove(move4);
-
-    effect[0].setMeshAssociation( 1,-1);  // mesh 0
-    effect[1].setMeshAssociation( 2,-1);  // mesh 1
-    effect[2].setMeshAssociation( 2,-1);  // mesh 1
-    effect[3].setMeshAssociation( 2,-1);  // mesh 1
-    effect[4].setMeshAssociation( 4,-1);  // mesh 2
-    effect[5].setMeshAssociation( 8,-1);  // mesh 3
-    effect[6].setMeshAssociation( 8,-1);  // mesh 3
-    effect[7].setMeshAssociation( 4,-1);  // mesh 2
-    effect[8].setMeshAssociation( 8,-1);  // mesh 3
-    effect[9].setMeshAssociation(15,-1);  // meshes 0,1,2,3
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsRediEdge()
-    {
-    Static3D move0 = new Static3D(0.0f, -0.5f, 0.0f);
-    Static3D move1 = new Static3D(0.25f, -0.25f, 0.0f);
-    Static3D move2 = new Static3D(0.5f, 0.0f, 0.0f);
-    Static3D move3 = new Static3D(0.0f, (SQ3-6)/8, (SQ3-6)/8);
-    Static3D flipZ = new Static3D(1,1,-1);
-    Static3D flipX = new Static3D(-1,1,1);
-    Static3D scale = new Static3D(2,2,2);
-    Static3D cent0 = new Static3D(0,0, 0);
-    Static3D cent1 = new Static3D(0,0, -1.5f);
-    Static3D axisX = new Static3D(1,0, 0);
-    Static3D axisY = new Static3D(0,1, 0);
-    Static3D axis  = new Static3D(0,SQ2/2,-SQ2/2);
-    Static1D angle1= new Static1D(90);
-    Static1D angle2= new Static1D(45);
-    Static1D angle3= new Static1D( (float)(180/Math.PI*Math.acos(SQ3/3)) );
-
-    VertexEffect[] effect = new VertexEffect[12];
-
-    effect[0] = new VertexEffectScale(scale);
-    effect[1] = new VertexEffectMove(move0);
-    effect[2] = new VertexEffectScale(flipZ);
-    effect[3] = new VertexEffectRotate(angle1,axisX,cent0);
-    effect[4] = new VertexEffectMove(move1);
-    effect[5] = new VertexEffectRotate(angle1,axisY,cent0);
-    effect[6] = new VertexEffectMove(move2);
-    effect[7] = new VertexEffectScale(flipX);
-    effect[8] = new VertexEffectRotate(angle2,axisX,cent0);
-    effect[9] = new VertexEffectMove(move3);
-    effect[10]= new VertexEffectRotate(angle3,axis ,cent1);
-    effect[11]= new VertexEffectScale(flipX);
-
-    effect[0].setMeshAssociation(63,-1);  // meshes 0,1,2,3,4,5
-    effect[1].setMeshAssociation( 3,-1);  // meshes 0,1
-    effect[2].setMeshAssociation( 2,-1);  // mesh 1
-    effect[3].setMeshAssociation( 2,-1);  // mesh 1
-    effect[4].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[5].setMeshAssociation(60,-1);  // meshes 2,3,4,5
-    effect[6].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[7].setMeshAssociation( 8,-1);  // mesh 3
-    effect[8].setMeshAssociation(48,-1);  // meshes 4,5
-    effect[9].setMeshAssociation(48,-1);  // meshes 4,5
-    effect[10].setMeshAssociation(48,-1); // meshes 4,5
-    effect[11].setMeshAssociation(32,-1); // mesh 5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsRediCorner()
-    {
-    Static3D axisY   = new Static3D(0,1,0);
-    Static3D axisX   = new Static3D(1,0,0);
-    Static3D axisZ   = new Static3D(0,0,1);
-    Static3D center  = new Static3D(0,0,0);
-    Static1D angle90 = new Static1D(90);
-    Static1D angle270= new Static1D(270);
-    Static1D angle45 = new Static1D(-45);
-    Static3D scale   = new Static3D(1.0f, SQ2, 1.0f);
-
-    VertexEffect[] effect = new VertexEffect[7];
-
-    effect[0] = new VertexEffectMove(new Static3D(0,0,+0.5f));
-    effect[1] = new VertexEffectRotate( angle270, axisX, center );
-    effect[2] = new VertexEffectRotate( angle90 , axisY, center );
-    effect[3] = new VertexEffectScale(scale);
-    effect[4] = new VertexEffectRotate( angle45 , axisX, center );
-    effect[5] = new VertexEffectRotate( angle90 , axisY, center );
-    effect[6] = new VertexEffectRotate( angle270, axisZ, center );
-
-    effect[0].setMeshAssociation( 7,-1);  // 0,1,2
-    effect[1].setMeshAssociation( 2,-1);  // 1
-    effect[2].setMeshAssociation( 4,-1);  // 2
-    effect[3].setMeshAssociation(56,-1);  // 3,4,5
-    effect[4].setMeshAssociation(56,-1);  // 3,4,5
-    effect[5].setMeshAssociation(16,-1);  // 4
-    effect[6].setMeshAssociation(32,-1);  // 5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsIvyCorner()
-    {
-    Static3D axisX  = new Static3D(1,0,0);
-    Static3D axisY  = new Static3D(0,1,0);
-    Static1D angle1 = new Static1D(+90);
-    Static1D angle2 = new Static1D(-90);
-    Static3D center = new Static3D(0,0,0);
-    Static3D move1  = new Static3D(IVY_M-0.5f,IVY_M-0.5f,0);
-
-    VertexEffect[] effect = new VertexEffect[5];
-
-    effect[0] = new VertexEffectScale(1/IVY_C);
-    effect[1] = new VertexEffectMove(move1);
-    effect[2] = new VertexEffectScale(new Static3D(1,1,-1));
-    effect[3] = new VertexEffectRotate(angle1,axisX,center);
-    effect[4] = new VertexEffectRotate(angle2,axisY,center);
-
-    effect[2].setMeshAssociation(54,-1);  // meshes 1,2,4,5
-    effect[3].setMeshAssociation(18,-1);  // meshes 1,4
-    effect[4].setMeshAssociation(36,-1);  // meshes 2,5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createVertexEffectsRexEdge()
-    {
-    float E = 0.5f - REX_D;
-    float F = 0.5f;
-    float G = (float)Math.sqrt(E*E+F*F);
-    float A = (float)((180/Math.PI)*Math.asin(E/G));
-
-    Static3D move1 = new Static3D(    0.0f, -E/3, 0.0f);
-    Static3D move2 = new Static3D(2*G/3 -F, +E/3, 0.0f);
-
-    Static3D center0= new Static3D(0.0f, 0.0f, 0.0f);
-    Static3D center1= new Static3D(  -F, 0.0f, 0.0f);
-    Static3D center2= new Static3D(  +F, 0.0f, 0.0f);
-    Static3D axisX  = new Static3D(1.0f, 0.0f, 0.0f);
-    Static3D axisY  = new Static3D(0.0f, 1.0f, 0.0f);
-    Static3D axisZ  = new Static3D(0.0f, 0.0f, 1.0f);
-
-    Static1D angle180 = new Static1D(180);
-    Static1D angle90  = new Static1D( 90);
-    Static1D angle270 = new Static1D(270);
-    Static1D angle1   = new Static1D(+A);
-    Static1D angle2   = new Static1D(-A);
-
-    VertexEffect[] effect = new VertexEffect[12];
-
-    effect[0] = new VertexEffectMove(move1);
-    effect[1] = new VertexEffectMove(move2);
-    effect[2] = new VertexEffectRotate(  angle90, axisX, center0 );
-    effect[3] = new VertexEffectRotate( angle270, axisX, center0 );
-    effect[4] = new VertexEffectRotate( angle180, axisX, center0 );
-    effect[5] = new VertexEffectRotate( angle180, axisY, center0 );
-    effect[6] = new VertexEffectScale ( new Static3D(-1, 1, 1) );
-    effect[7] = new VertexEffectScale ( new Static3D( 1,-1, 1) );
-    effect[8] = new VertexEffectRotate(   angle1, axisY, center1);
-    effect[9] = new VertexEffectRotate(   angle2, axisY, center2);
-    effect[10]= new VertexEffectRotate(   angle2, axisZ, center1);
-    effect[11]= new VertexEffectRotate(   angle1, axisZ, center2);
-
-    effect[0].setMeshAssociation( 3,-1);  // meshes 0 & 1
-    effect[1].setMeshAssociation(60,-1);  // meshes 2,3,4,5
-    effect[2].setMeshAssociation( 2,-1);  // meshes 1
-    effect[3].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[4].setMeshAssociation(48,-1);  // meshes 4,5
-    effect[5].setMeshAssociation(32,-1);  // mesh 5
-    effect[6].setMeshAssociation( 8,-1);  // apply to mesh 3
-    effect[7].setMeshAssociation( 2,-1);  // apply to mesh 1
-    effect[8].setMeshAssociation(16,-1);  // apply to mesh 4
-    effect[9].setMeshAssociation(32,-1);  // apply to mesh 5
-    effect[10].setMeshAssociation(4,-1);  // apply to mesh 2
-    effect[11].setMeshAssociation(8,-1);  // apply to mesh 3
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    // compute the normal vector
+    if( foundIndex==-1 )
+      {
+      throw new RuntimeException("all vertices colinear");
+      }
 
-  VertexEffect[] createVertexEffectsRexCorner()
-    {
-    Static3D center= new Static3D(0.0f, 0.0f, 0.0f);
-    Static3D axisZ = new Static3D(0.0f, 0.0f, 1.0f);
-    Static1D angle = new Static1D(45);
+    computeNormalVector(vert3D,0,1,foundIndex);
+
+    // rotate so that the normal vector becomes (0,0,1)
+    float axisX = -mBuffer[1];
+    float axisY =  mBuffer[0];
+    float axisZ = 0.0f;
+
+    float vecLen = mBuffer[0]*mBuffer[0] + mBuffer[1]*mBuffer[1] + mBuffer[2]*mBuffer[2];
+    vecLen = (float)Math.sqrt(vecLen);
+    mBuffer[0] /= vecLen;
+    mBuffer[1] /= vecLen;
+    mBuffer[2] /= vecLen;
+
+    float axiLen = axisX*axisX + axisY*axisY + axisZ*axisZ;
+    axiLen = (float)Math.sqrt(axiLen);
+    axisX /= axiLen;
+    axisY /= axiLen;
+    axisZ /= axiLen;
+
+    float cosTheta = mBuffer[2];
+    float sinTheta = axiLen / vecLen;
+
+    mQuat1[0] = axisX*sinTheta;
+    mQuat1[1] = axisY*sinTheta;
+    mQuat1[2] = axisZ*sinTheta;
+    mQuat1[3] = cosTheta;
+    mQuat2[0] = axisX*sinTheta;
+    mQuat2[1] = axisY*sinTheta;
+    mQuat2[2] = axisZ*sinTheta;
+    mQuat2[3] = -cosTheta;
+
+    for (float[] vert : vert3D)
+      {
+      quatMultiply(mQuat1, vert, mQuat3);
+      quatMultiply(mQuat3, mQuat2, vert);
+      }
 
-    VertexEffect[] effect = new VertexEffect[1];
-    effect[0] = new VertexEffectRotate(angle, axisZ, center);
+    // fit the whole thing in a square and remember the scale & 2D vertices
+    fitInSquare(info, vert3D);
 
-    return effect;
+    // remember the rotation
+    info.qx = mQuat1[0];
+    info.qy = mQuat1[1];
+    info.qz = mQuat1[2];
+    info.qw =-mQuat1[3];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  VertexEffect[] createVertexEffectsKilominxCorner()
+  private float computeCos(float x1, float y1, float x2, float y2, float len1, float len2)
     {
-    VertexEffect[] effect = new VertexEffect[9];
-
-    float H = 0.5f*(SIN54 / COS54);
-    float Y1= (float)(Math.sqrt(2+0.4f*SQ5)/4);
-    float Y2= H/(2*COS_HALFD);
-    float A = (float)(Math.acos(-SQ5/5)*180/Math.PI);  // dihedral angle of a dedecahedron in degrees
-    float sin18 = SIN18;
-    float cos18 = (float)(Math.sqrt(1- SIN18 * SIN18));
-    float LEN   = (float)Math.sqrt(H*H/(COS_HALFD*COS_HALFD) + 0.25f);
-
-    Static3D axisZ = new Static3D(0.0f  , 0.0f , 1.0f);
-    Static3D axisY = new Static3D(0.0f  , 1.0f , 0.0f);
-    Static3D axisA = new Static3D(-sin18, cos18, 0.0f);
-    Static3D axisC = new Static3D( H/LEN, -0.5f/LEN,-H*SIN_HALFD/(COS_HALFD*LEN));
-
-    Static3D move1 = new Static3D(0,-Y1,0);
-    Static3D move2 = new Static3D(0,-Y2,0);
-    Static3D move3 = new Static3D(0.5f*cos18,0.5f*sin18,0);
-    Static3D center= new Static3D(0.0f, 0.0f, 0.0f);
-
-    Static1D angle1 = new Static1D(54);
-    Static1D angle2 = new Static1D(A/2+18);
-    Static1D angle3 = new Static1D(90);
-    Static1D angle4 = new Static1D(120);
-    Static1D angle5 = new Static1D(240);
-    Static1D angle6 = new Static1D(90-A/2);
-
-    effect[0] = new VertexEffectMove(move1);
-    effect[1] = new VertexEffectMove(move2);
-    effect[2] = new VertexEffectRotate(angle1, axisZ, center);
-    effect[3] = new VertexEffectRotate(angle2, axisZ, center);
-    effect[4] = new VertexEffectRotate(angle3, axisA, center);
-    effect[5] = new VertexEffectMove(move3);
-    effect[6] = new VertexEffectRotate(angle4, axisC, center);
-    effect[7] = new VertexEffectRotate(angle5, axisC, center);
-    effect[8] = new VertexEffectRotate(angle6, axisY, center);
-
-    effect[0].setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect[1].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[2].setMeshAssociation( 7,-1);  // meshes 0,1,2
-    effect[3].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[4].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[5].setMeshAssociation(56,-1);  // meshes 3,4,5
-    effect[6].setMeshAssociation(18,-1);  // meshes 1,4
-    effect[7].setMeshAssociation(36,-1);  // meshes 2,5
-
-    return effect;
+    return (x1*x2+y1*y2) / (len1*len2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// sin of (signed!) angle between vectors (x1,y1) and (x2,y2), counterclockwise!
 
-  VertexEffect[] createVertexEffectsMinxCorner(float width)
+  private float computeSin(float x1, float y1, float x2, float y2, float len1, float len2)
     {
-    VertexEffect[] effect = new VertexEffect[9];
-
-    float Y = COS54/(2*SIN54);
-
-    float sinA = (2*SIN54*SIN54-1)/COS54;
-    float cosA = (float)Math.sqrt(1-sinA*sinA);
-    float LEN  = 0.5f/SIN54;
-    float scale= width/LEN;
-
-    Static3D axisA = new Static3D( SIN54, COS54, 0.0f);
-    Static3D axisB = new Static3D(-SIN54, COS54, 0.0f);
-    Static3D axisX = new Static3D(  1.0f,  0.0f, 0.0f);
-
-    Static3D centerU = new Static3D( 0.0f, Y, 0.0f);
-    Static3D centerD = new Static3D( 0.0f,-Y, 0.0f);
-
-    Static3D move1= new Static3D(0.0f, -sinA*LEN, -cosA*LEN );
-    Static3D move2= new Static3D(0.0f, Y , 0.0f );
-
-    Static1D angleD = new Static1D(DIHEDRAL1);
-    Static1D angleE = new Static1D(360-DIHEDRAL1);
-    Static1D angleF = new Static1D(DIHEDRAL2);
-
-    effect[0] = new VertexEffectScale ( new Static3D( 1, 1,-1) );
-    effect[1] = new VertexEffectRotate(angleE, axisA, centerU);
-    effect[2] = new VertexEffectRotate(angleD, axisB, centerU);
-    effect[3] = new VertexEffectMove(move1);
-    effect[4] = new VertexEffectRotate(angleE, axisA, centerD);
-    effect[5] = new VertexEffectRotate(angleD, axisB, centerD);
-    effect[6] = new VertexEffectRotate(angleF, axisX, centerD);
-    effect[7] = new VertexEffectMove(move2);
-    effect[8] = new VertexEffectScale(scale);
-
-    effect[0].setMeshAssociation(  3,-1);  // meshes 0,1
-    effect[1].setMeshAssociation( 16,-1);  // mesh 4
-    effect[2].setMeshAssociation( 32,-1);  // mesh 5
-    effect[3].setMeshAssociation( 56,-1);  // meshes 3,4,5
-    effect[4].setMeshAssociation(  1,-1);  // mesh 0
-    effect[5].setMeshAssociation(  2,-1);  // mesh 1
-
-    return effect;
+    return (x2*y1-x1*y2) / (len1*len2);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  VertexEffect[] createVertexEffectsKilominxEdge(float width, float height, boolean left)
+  private void rotateAllVertices(float[] result, int len, float[] vertices, float sin, float cos)
     {
-    VertexEffect[] effect = new VertexEffect[11 + (left ? 0:1)];
-
-    float D = height/COS18;
-    float W = D*SIN18;
-    float X1 = height/2;
-    float Y1 = width/2;
-    float Y2 = (width+W)/2;
-    float Y3 = D*COS54;
-    float Y4 = height*COS_HALFD;
-    float Z = 2*height*COS_HALFD;
-    float alpha = 90-DIHEDRAL1/2;
-    float beta  = DIHEDRAL2;
-
-    Static1D angle1 = new Static1D(alpha);
-    Static1D angle2 = new Static1D(180-alpha);
-    Static1D angle3 = new Static1D(beta);
-    Static1D angle4 = new Static1D(90);
-
-    Static3D move1 = new Static3D(+X1,-Y1,0);
-    Static3D move2 = new Static3D(-X1,-Y2+W,-Z);
-    Static3D move3 = new Static3D(0,+Y3,0);
-    Static3D move4 = new Static3D(0,-Y4-width,0);
-    Static3D scale = new Static3D(+1,+1,-1);
-
-    Static3D axisXplus = new Static3D(+1, 0, 0);
-    Static3D axisYplus = new Static3D( 0,+1, 0);
-
-    Static3D center1= new Static3D( 0, 0, 0);
-    Static3D center2= new Static3D( 0, 0,-Z);
-    Static3D center3= new Static3D( 0,-width, 0);
-
-    effect[ 0] = new VertexEffectMove(move1);
-    effect[ 1] = new VertexEffectMove(move2);
-    effect[ 2] = new VertexEffectMove(move3);
-    effect[ 3] = new VertexEffectMove(move4);
-    effect[ 4] = new VertexEffectScale(scale);
-    effect[ 5] = new VertexEffectRotate(angle1, axisYplus , center1);
-    effect[ 6] = new VertexEffectRotate(angle2, axisYplus , center1);
-    effect[ 7] = new VertexEffectRotate(angle1, axisYplus , center2);
-    effect[ 8] = new VertexEffectRotate(angle2, axisYplus , center2);
-    effect[ 9] = new VertexEffectRotate(angle3, axisXplus , center1);
-    effect[10] = new VertexEffectRotate(angle4, axisXplus , center3);
-
-    if( !left )
+    for(int i=0; i<len; i++)
       {
-      Static3D scale1 = new Static3D(+1,-1,+1);
-      effect[11] = new VertexEffectScale(scale1);
+      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;
       }
-
-    effect[ 0].setMeshAssociation( 3,-1);  // meshes 0,1
-    effect[ 1].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[ 2].setMeshAssociation(16,-1);  // mesh 4
-    effect[ 3].setMeshAssociation(32,-1);  // mesh 5
-    effect[ 4].setMeshAssociation( 2,-1);  // mesh 1
-    effect[ 5].setMeshAssociation( 1,-1);  // mesh 0
-    effect[ 6].setMeshAssociation( 2,-1);  // mesh 1
-    effect[ 7].setMeshAssociation( 4,-1);  // mesh 2
-    effect[ 8].setMeshAssociation( 8,-1);  // mesh 3
-    effect[ 9].setMeshAssociation(16,-1);  // mesh 4
-    effect[10].setMeshAssociation(32,-1);  // mesh 5
-
-    return effect;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  VertexEffect[] createVertexEffectsMegaminxEdge(float width, float height)
+  private boolean isScaledVersionOf(float[] v1, float[] v2, int len)
     {
-    VertexEffect[] effect = new VertexEffect[11];
-
-    float X = 0.5f*height;
-    float Y = height*(COS54/COS18) + width*0.5f;
-    float Z = 2*height*COS_HALFD;
-
-    float alpha = 90-DIHEDRAL1/2;
-    float beta  = DIHEDRAL2;
-
-    Static1D angle1 = new Static1D(alpha);
-    Static1D angle2 = new Static1D(180-alpha);
-    Static1D angle3 = new Static1D(beta);
-
-    Static3D move1 = new Static3D(X,0,0);
-    Static3D move2 = new Static3D(X,0,-Z);
-    Static3D move3 = new Static3D(0,+Y,0);
-    Static3D move4 = new Static3D(0,-Y,0);
-    Static3D scale = new Static3D(+1,+1,-1);
-
-    Static3D axisXplus = new Static3D(+1, 0, 0);
-    Static3D axisXminus= new Static3D(-1, 0, 0);
-    Static3D axisYplus = new Static3D( 0,+1, 0);
-    Static3D axisYminus= new Static3D( 0,-1, 0);
-
-    Static3D center1= new Static3D( 0, 0, 0);
-    Static3D center2= new Static3D( 0, 0,-Z);
-    Static3D center3= new Static3D( 0,+width*0.5f, 0);
-    Static3D center4= new Static3D( 0,-width*0.5f, 0);
-
-    effect[ 0] = new VertexEffectMove(move1);
-    effect[ 1] = new VertexEffectMove(move2);
-    effect[ 2] = new VertexEffectMove(move3);
-    effect[ 3] = new VertexEffectMove(move4);
-    effect[ 4] = new VertexEffectScale(scale);
-    effect[ 5] = new VertexEffectRotate(angle1, axisYplus , center1);
-    effect[ 6] = new VertexEffectRotate(angle2, axisYplus , center1);
-    effect[ 7] = new VertexEffectRotate(angle1, axisYminus, center2);
-    effect[ 8] = new VertexEffectRotate(angle2, axisYminus, center2);
-    effect[ 9] = new VertexEffectRotate(angle3, axisXplus , center3);
-    effect[10] = new VertexEffectRotate(angle3, axisXminus, center4);
-
-    effect[ 0].setMeshAssociation( 3,-1);  // meshes 0,1
-    effect[ 1].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[ 2].setMeshAssociation(16,-1);  // mesh 4
-    effect[ 3].setMeshAssociation(32,-1);  // mesh 5
-    effect[ 4].setMeshAssociation( 2,-1);  // mesh 1
-    effect[ 5].setMeshAssociation( 1,-1);  // mesh 0
-    effect[ 6].setMeshAssociation( 2,-1);  // mesh 1
-    effect[ 7].setMeshAssociation( 4,-1);  // mesh 2
-    effect[ 8].setMeshAssociation( 8,-1);  // mesh 3
-    effect[ 9].setMeshAssociation(16,-1);  // mesh 4
-    effect[10].setMeshAssociation(32,-1);  // mesh 5
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  VertexEffect[] createCuboidEffects(int[] dimensions)
-    {
-    float X = dimensions[0];
-    float Y = dimensions[1];
-    float Z = dimensions[2];
-
-    float MAX_XY = Math.max(X,Y);
-    float MAX_XZ = Math.max(X,Z);
-    float MAX_YZ = Math.max(Z,Y);
-
-    Static1D angle = new Static1D(90);
-    Static3D move  = new Static3D( 0.0f, 0.0f, 0.5f);
-    Static3D axisX = new Static3D( 1.0f, 0.0f, 0.0f);
-    Static3D axisY = new Static3D( 0.0f, 1.0f, 0.0f);
-    Static3D center= new Static3D( 0.0f, 0.0f, 0.0f);
-
-    Static3D scale3 = new Static3D(MAX_XY,MAX_XY,+Z);
-    Static3D scale4 = new Static3D(MAX_XY,MAX_XY,-Z);
-    Static3D scale5 = new Static3D(MAX_XZ,+Y,MAX_XZ);
-    Static3D scale6 = new Static3D(MAX_XZ,-Y,MAX_XZ);
-    Static3D scale7 = new Static3D(+X,MAX_YZ,MAX_YZ);
-    Static3D scale8 = new Static3D(-X,MAX_YZ,MAX_YZ);
-
-    VertexEffect[] effect = new VertexEffect[9];
-
-    effect[0] = new VertexEffectMove(move);
-    effect[1] = new VertexEffectRotate(angle, axisX, center);
-    effect[2] = new VertexEffectRotate(angle, axisY, center);
-    effect[3] = new VertexEffectScale(scale3);
-    effect[4] = new VertexEffectScale(scale4);
-    effect[5] = new VertexEffectScale(scale5);
-    effect[6] = new VertexEffectScale(scale6);
-    effect[7] = new VertexEffectScale(scale7);
-    effect[8] = new VertexEffectScale(scale8);
-
-    effect[1].setMeshAssociation(12,-1);  // meshes 2,3
-    effect[2].setMeshAssociation( 3,-1);  // meshes 0,1
-    effect[3].setMeshAssociation(16,-1);  // mesh 4
-    effect[4].setMeshAssociation(32,-1);  // mesh 5
-    effect[5].setMeshAssociation( 8,-1);  // mesh 3
-    effect[6].setMeshAssociation( 4,-1);  // mesh 2
-    effect[7].setMeshAssociation( 1,-1);  // mesh 0
-    effect[8].setMeshAssociation( 2,-1);  // mesh 1
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// OBJECTS
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// CUBE
-
-  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);
+    float EPSILON = 0.001f;
+    float scale = v1[0]!=0.0f ? v2[0]/v1[0] : v2[1]/v1[1];
 
-    roundCorners(mesh,roundingCenter,vertices,0.06f,0.12f);
+    for(int i=1; i<len; i++)
+      {
+      float horz = v2[2*i  ] - scale*v1[2*i  ];
+      float vert = v2[2*i+1] - scale*v1[2*i+1];
 
-    mesh.mergeEffComponents();
+      if( horz>EPSILON || horz<-EPSILON || vert>EPSILON || vert<-EPSILON ) return false;
+      }
 
-    return mesh;
+    return true;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// SKEWB
 
-  MeshBase createSkewbCornerMesh()
+  private void mirrorAllVertices(float[] output, int len, float[] input)
     {
-    MeshBase mesh = createFacesSkewbCorner();
-    VertexEffect[] effects = createVertexEffectsSkewbCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float E = 0.5f;
-    Static3D roundingCenter = new Static3D(-E/2,-E/2,-E/2);
-
-    Static3D[] verticesType1 = new Static3D[1];
-    verticesType1[0] = new Static3D(0.0f,0.0f,0.0f);
-    roundCorners(mesh,roundingCenter,verticesType1,0.08f,0.15f);
-
-    Static3D[] verticesType2 = new Static3D[3];
-    verticesType2[0] = new Static3D(-E, 0, 0);
-    verticesType2[1] = new Static3D( 0,-E, 0);
-    verticesType2[2] = new Static3D( 0, 0,-E);
-    roundCorners(mesh,roundingCenter,verticesType2,0.08f,0.20f);
-
-    mesh.mergeEffComponents();
-
-    return mesh;
+    for(int vertex=0; vertex<len; vertex++)
+      {
+      output[2*vertex  ] = input[2*vertex  ];
+      output[2*vertex+1] =-input[2*vertex+1];
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createSkewbFaceMesh()
+  private void correctInfo(FaceInfo info, float[] rotatedVertices, int len, float[] originalVertices, float sin, float cos, boolean flip)
     {
-    MeshBase mesh = createFacesSkewbFace();
-    VertexEffect[] effects = createVertexEffectsSkewbFace();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    Static3D roundingCenter = new Static3D(0,0,-0.2f);
-    float E = SQ2/4;
-    Static3D[] vertices = new Static3D[4];
-    vertices[0] = new Static3D(-E*SQ2,      0, 0);
-    vertices[1] = new Static3D(+E*SQ2,      0, 0);
-    vertices[2] = new Static3D(     0, -E*SQ2, 0);
-    vertices[3] = new Static3D(     0, +E*SQ2, 0);
-    roundCorners(mesh,roundingCenter,vertices,0.06f,0.10f);
-
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-
-    return mesh;
-    }
+    info.flip = flip;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// SKEWB DIAMOND / PYRAMINX
+    System.arraycopy(originalVertices, 0, info.vertices, 0, 2*len);
 
-  MeshBase createOctaMesh()
-    {
-    MeshBase mesh = createFacesOcta();
-    VertexEffect[] effects = createVertexEffectsOcta();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
+    float scale = rotatedVertices[0]!=0.0f ? originalVertices[0]/rotatedVertices[0] :
+                                             originalVertices[1]/rotatedVertices[1];
+    info.scale *= scale;
 
-    Static3D roundingCenter = new Static3D(0,0,0);
-    Static3D[] vertices = new Static3D[6];
-    vertices[0] = new Static3D(    0, SQ2/2,    0 );
-    vertices[1] = new Static3D( 0.5f,     0, 0.5f );
-    vertices[2] = new Static3D(-0.5f,     0, 0.5f );
-    vertices[3] = new Static3D(    0,-SQ2/2,    0 );
-    vertices[4] = new Static3D(-0.5f,     0,-0.5f );
-    vertices[5] = new Static3D( 0.5f,     0,-0.5f );
+    mQuat1[0] = 0.0f;
+    mQuat1[1] = 0.0f;
+    mQuat1[2] = sin;
+    mQuat1[3] = cos;
 
-    roundCorners(mesh,roundingCenter,vertices,0.06f,0.20f);
+    mQuat2[0] = info.qx;
+    mQuat2[1] = info.qy;
+    mQuat2[2] = info.qz;
+    mQuat2[3] = info.qw;
 
-    mesh.mergeEffComponents();
+    quatMultiply( mQuat1, mQuat2, mQuat3 );
 
-    return mesh;
+    info.qx = mQuat3[0];
+    info.qy = mQuat3[1];
+    info.qz = mQuat3[2];
+    info.qw = mQuat3[3];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createTetraMesh()
+  private boolean foundVertex(FaceInfo info, float[] buffer, int len, boolean inverted, float[] vertices, float[] vert2D, float lenVert)
     {
-    MeshBase mesh = createFacesTetra();
-    VertexEffect[] effects = createVertexEffectsTetra();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
+    for(int vertex=0; vertex<len; vertex++)
+      {
+      float xR = vertices[2*vertex  ];
+      float yR = vertices[2*vertex+1];
+      float lenRotV = (float)Math.sqrt(xR*xR+yR*yR);
+      float cos = computeCos(xR,yR,vert2D[0],vert2D[1], lenRotV, lenVert);
+      float sin = computeSin(xR,yR,vert2D[0],vert2D[1], lenRotV, lenVert);
 
-    Static3D roundingCenter = new Static3D(0,0,0);
-    Static3D[] verticesRound = new Static3D[4];
-    verticesRound[0] = new Static3D(-0.5f,+SQ2/4,   0 );
-    verticesRound[1] = new Static3D(+0.5f,+SQ2/4,   0 );
-    verticesRound[2] = new Static3D(    0,-SQ2/4,+0.5f);
-    verticesRound[3] = new Static3D(    0,-SQ2/4,-0.5f);
-    roundCorners(mesh,roundingCenter,verticesRound,0.08f,0.15f);
+      rotateAllVertices(buffer,len,vertices,sin,cos);
 
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
+      if( isScaledVersionOf(buffer,vert2D,len) )
+        {
+        correctInfo(info,buffer,len,vert2D,sin,cos,inverted);
+        return true;
+        }
+      }
 
-    return mesh;
+    return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// DINO
 
-  MeshBase createDinoMesh()
+  private boolean tryFindingRotation(final FaceInfo info, final float[] vert2D)
     {
-    MeshBase mesh = createFacesDino();
-    VertexEffect[] effects = createVertexEffectsDino();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float F = 0.5f;
-    Static3D roundingCenter = new Static3D(0.0f, -1.5f*F, -1.5f*F);
-    Static3D[] verticesRound = new Static3D[4];
-    verticesRound[0] = new Static3D(     0,-3*F,    0 );
-    verticesRound[1] = new Static3D(     0,   0, -3*F );
-    verticesRound[2] = new Static3D(  -3*F,   0,    0 );
-    verticesRound[3] = new Static3D(  +3*F,   0,    0 );
-    roundCorners(mesh,roundingCenter,verticesRound,0.10f,0.40f);
+    int len = vert2D.length/2;
 
-    mesh.mergeEffComponents();
+    if( len == info.vertices.length/2 )
+      {
+      float[] tmp1 = new float[2*len];
+      float lenVert = (float)Math.sqrt(vert2D[0]*vert2D[0] + vert2D[1]*vert2D[1]);
+      if( foundVertex(info,tmp1,len,false,info.vertices,vert2D,lenVert) ) return true;
+      float[] tmp2 = new float[2*len];
+      mirrorAllVertices(tmp2,len,info.vertices);
+      if( foundVertex(info,tmp1,len,true ,tmp2         ,vert2D,lenVert) ) return true;
+      }
 
-    return mesh;
+    return false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Helicopter
 
-  MeshBase createHelicopterCornerMesh()
+  private float[][] constructVert(float[][] vertices, int[] index)
     {
-    MeshBase mesh = createFacesHelicopterCorner();
-    VertexEffect[] effects = createVertexEffectsHelicopterCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float E = 0.5f;
-    Static3D roundingCenter = new Static3D(-E/2,-E/2,-E/2);
-
-    Static3D[] verticesType1 = new Static3D[1];
-    verticesType1[0] = new Static3D(0.0f,0.0f,0.0f);
-    roundCorners(mesh,roundingCenter,verticesType1,0.08f,0.15f);
+    int len = index.length;
+    float[][] ret = new float[len][4];
 
-    Static3D[] verticesType2 = new Static3D[3];
-    verticesType2[0] = new Static3D(-E, 0, 0);
-    verticesType2[1] = new Static3D( 0,-E, 0);
-    verticesType2[2] = new Static3D( 0, 0,-E);
-    roundCorners(mesh,roundingCenter,verticesType2,0.08f,0.20f);
-
-    mesh.mergeEffComponents();
+    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 mesh;
+    return ret;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  MeshBase createHelicopterFaceMesh()
+  private void prepareFaceInfo( final float[][] vertices, final int[][] indexes)
     {
-    MeshBase mesh = createFacesHelicopterFace();
-    VertexEffect[] effects = createVertexEffectsHelicopterFace();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float E = 0.5f;
-    Static3D roundingCenter = new Static3D(-E/2 + E/3,-E/2 + E/3,-E/2);
+    mFaceInfo.clear();
 
-    Static3D[] verticesType1 = new Static3D[1];
-    verticesType1[0] = new Static3D(E/3,E/3,0.0f);
-    roundCorners(mesh,roundingCenter,verticesType1,0.06f,0.15f);
+    int numFaces = indexes.length;
+    FaceInfo info;
 
-    Static3D[] verticesType2 = new Static3D[2];
-    verticesType2[0] = new Static3D(-E+E/3, E/3  , 0);
-    verticesType2[1] = new Static3D( E/3  ,-E+E/3, 0);
-    roundCorners(mesh,roundingCenter,verticesType2,0.08f,0.20f);
+    for(int face=0; face<numFaces; face++)
+      {
+      FaceInfo newInfo = new FaceInfo();
+      int[] index = indexes[face];
+      float[][] vert = constructVert(vertices,index);
+      constructNew(newInfo,vert);
 
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
+      for(int previous=0; previous<face; previous++)
+        {
+        info = mFaceInfo.get(previous);
+        if( tryFindingRotation(info,newInfo.vertices) ) break;
+        }
 
-    return mesh;
+      mFaceInfo.add(newInfo);
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Redi cube
 
-  MeshBase createRediEdgeMesh()
+  MeshBase createRoundedSolid(final float[][] vertices, final int[][] vertIndexes, final float[][] bands, final int[] bandIndexes)
     {
-    MeshBase mesh = createFacesRediEdge();
-    VertexEffect[] effects = createVertexEffectsRediEdge();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
+    int EFFECTS_PER_FACE = 3;
 
-    Static3D center = new Static3D(0.0f,-0.75f,-0.75f);
-    Static3D[] vertices = new Static3D[2];
-    vertices[0] = new Static3D( 0.5f, 0.0f, 0.0f);
-    vertices[1] = new Static3D(-0.5f, 0.0f, 0.0f);
-    roundCorners(mesh,center,vertices,0.06f,0.20f);
+    prepareFaceInfo(vertices,vertIndexes);
 
-    mesh.mergeEffComponents();
+    int numFaces = vertIndexes.length;
+    float[] band, bandsComputed;
+    MeshBase[] meshes = new MeshBase[numFaces];
+    FaceInfo info;
 
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createRediCornerMesh()
-    {
-    MeshBase mesh = createFacesRediCorner();
-    VertexEffect[] effects = createVertexEffectsRediCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
+    for(int face=0; face<numFaces; face++)
+      {
+      info = mFaceInfo.get(face);
+      band = bands[bandIndexes[face]];
+      bandsComputed = computeBands( band[0], (int)band[1], band[2], band[3], (int)band[4]);
+      meshes[face] = new MeshPolygon(info.vertices,bandsComputed,(int)band[5],(int)band[6]);
+      meshes[face].setEffectAssociation(0,(1<<face),0);
+      }
 
+    MeshBase mesh = new MeshJoined(meshes);
+    VertexEffect[] effects = new VertexEffect[EFFECTS_PER_FACE*numFaces];
     Static3D center = 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,center,vertices,0.06f,0.12f);
-
-    mesh.mergeEffComponents();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createIvyCornerMesh()
-    {
-    MeshBase mesh = createFacesIvyCorner();
-    VertexEffect[] effects = createVertexEffectsIvyCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    Static3D center = new Static3D(-0.5f,-0.5f,-0.5f);
-    Static3D[] vertices = new Static3D[4];
-    vertices[0] = new Static3D(+0.0f,+0.0f,+0.0f);
-    vertices[1] = new Static3D(-1.0f,+0.0f,+0.0f);
-    vertices[2] = new Static3D(+0.0f,-1.0f,+0.0f);
-    vertices[3] = new Static3D(+0.0f,+0.0f,-1.0f);
-
-    roundCorners(mesh,center,vertices,0.03f,0.10f);
-
-    mesh.mergeEffComponents();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createIvyFaceMesh()
-    {
-    MeshBase mesh = createFacesIvyFace();
-
-    Static3D center = new Static3D(-0.0f,-0.0f,-0.5f);
-    Static3D[] vertices = new Static3D[2];
-    vertices[0] = new Static3D(-0.5f,+0.5f,+0.0f);
-    vertices[1] = new Static3D(+0.5f,-0.5f,+0.0f);
-
-    roundCorners(mesh,center,vertices,0.03f,0.10f);
-
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createRexCornerMesh()
-    {
-    MeshBase mesh = createFacesRexCorner();
-    VertexEffect[] effects = createVertexEffectsRexCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    Static3D center = new Static3D(0.0f,0.0f,-0.25f);
-    Static3D[] vertices = new Static3D[1];
-    vertices[0] = new Static3D(+0.25f,+0.25f,+0.0f);
-    roundCorners(mesh,center,vertices,0.03f,0.10f);
-
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createRexFaceMesh()
-    {
-    MeshBase mesh = createFacesRexFace();
-
-    mesh.mergeEffComponents();
-    mesh.addEmptyTexComponent();
-    mesh.addEmptyTexComponent();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createRexEdgeMesh()
-    {
-    MeshBase mesh = createFacesRexEdge();
-    VertexEffect[] effects = createVertexEffectsRexEdge();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    Static3D center = new Static3D(0.0f,-0.5f,-0.5f);
-    Static3D[] vertices = new Static3D[2];
-    vertices[0] = new Static3D(+0.5f,+0.0f,+0.0f);
-    vertices[1] = new Static3D(-0.5f,+0.0f,+0.0f);
-    roundCorners(mesh,center,vertices,0.06f,0.10f);
-
-    mesh.mergeEffComponents();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createKilominxCornerMesh()
-    {
-    MeshBase mesh = createFacesKilominxCorner();
-    VertexEffect[] effects = createVertexEffectsKilominxCorner();
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float A = (2*SQ3/3)* SIN54;
-    float B = 0.4f;
-    float X = SIN_HALFD* SIN54 * COS54;
-    float Y = SIN54 * SIN54 - 0.5f;
-    float Z = COS_HALFD* SIN54 * COS54;
-
-    Static3D center = new Static3D(0.0f, -(float)Math.sqrt(1-A*A)*B,-A*B);
-
-    Static3D[] vertices = new Static3D[4];
-    vertices[0] = new Static3D( 0.0f, 0.0f, 0.0f);
-    vertices[1] = new Static3D( 0.0f,-0.5f, 0.0f);
-    vertices[2] = new Static3D(-X   , Y   ,-Z   );
-    vertices[3] = new Static3D(+X   , Y   ,-Z   );
-
-    roundCorners(mesh,center,vertices,0.03f,0.10f);
-
-    //mesh.mergeEffComponents();
 
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createKilominxEdgeMesh(int numLayers, float width, float height, boolean left)
-    {
-    MeshBase mesh = createFacesKilominxEdge(numLayers,width,height);
-    VertexEffect[] effects = createVertexEffectsKilominxEdge(width,height,left);
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float A = (2*SQ3/3)* SIN54;
-    float B = 0.4f;
-    float X = SIN_HALFD* SIN54 * COS54;
-    float Y = SIN54 * SIN54 - 0.5f;
-    float Z = COS_HALFD* SIN54 * COS54;
-    float S = 2*width;
-
-    Static3D center = new Static3D(0.0f, -(float)Math.sqrt(1-A*A)*B,-A*B);
-    Static3D[] vertices = new Static3D[4];
-    vertices[0] = new Static3D( 0.0f, 0.0f  , 0.0f);
-    vertices[1] = new Static3D( 0.0f,-0.5f*S, 0.0f);
-    vertices[2] = new Static3D(-X*S , Y*S   ,-Z*S );
-    vertices[3] = new Static3D(+X*S , Y*S   ,-Z*S );
-
-    roundCorners(mesh,center,vertices,0.04f,0.10f);
-
-    //mesh.mergeEffComponents();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createMinxCornerMesh()
-    {
-    int numLayers = 9;
-    float width = (numLayers/3.0f)*(0.5f-MEGA_D)/(0.5f*(numLayers-1));
-
-    MeshBase mesh = createFacesMinxCorner(numLayers);
-    VertexEffect[] effects = createVertexEffectsMinxCorner(width);
-    for( VertexEffect effect : effects ) mesh.apply(effect);
-
-    float A = (2*SQ3/3)* SIN54;
-    float B = 0.4f;
-    Static3D center = new Static3D(0.0f, -(float)Math.sqrt(1-A*A)*B,-A*B);
-    Static3D[] vertices = new Static3D[1];
-    vertices[0] = new Static3D( 0.0f, 0.0f  , 0.0f);
-    roundCorners(mesh,center,vertices,0.04f,0.10f);
-
-    //mesh.mergeEffComponents();
-
-    return mesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  MeshBase createMegaminxEdgeMesh(float width, float height)
-    {
-    MeshBase mesh = createFacesMegaminxEdge(width,height);
-    VertexEffect[] effects = createVertexEffectsMegaminxEdge(width,height);
-    for( VertexEffect effect : effects ) mesh.apply(effect);
+    for(int face=0; face<numFaces; face++)
+      {
+      int assoc = (1<<face);
+      info = mFaceInfo.get(face);
 
-    //mesh.mergeEffComponents();
+      Static3D move3D= new Static3D(info.vx,info.vy,info.vz);
+      Static3D scale = new Static3D(info.scale,info.scale, info.flip ? -info.scale : info.scale);
+      Static4D quat  = new Static4D(info.qx,info.qy,info.qz,info.qw);
 
-    return mesh;
-    }
+      effects[EFFECTS_PER_FACE*face  ] = new VertexEffectScale(scale);
+      effects[EFFECTS_PER_FACE*face+1] = new VertexEffectQuaternion(quat,center);
+      effects[EFFECTS_PER_FACE*face+2] = new VertexEffectMove(move3D);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+      effects[EFFECTS_PER_FACE*face  ].setMeshAssociation(assoc,-1);
+      effects[EFFECTS_PER_FACE*face+1].setMeshAssociation(assoc,-1);
+      effects[EFFECTS_PER_FACE*face+2].setMeshAssociation(assoc,-1);
+      }
 
-  MeshBase createCuboidMesh(int[] dimensions)
-    {
-    MeshBase mesh = createCuboid(dimensions);
-    VertexEffect[] effects = createCuboidEffects(dimensions);
     for( VertexEffect effect : effects ) mesh.apply(effect);
 
-    int X = dimensions[0];
-    int Y = dimensions[1];
-    int Z = dimensions[2];
-
-    float strength = 0.04f;
-    float radius   = 0.15f;
-
-    Static3D[] vertices = new Static3D[1];
-    Static3D center;
-
-    vertices[0] = new Static3D(+0.5f*X,+0.5f*Y,+0.5f*Z);
-    center = new Static3D(+0.5f*(X-1),+0.5f*(Y-1),+0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(+0.5f*X,+0.5f*Y,-0.5f*Z);
-    center = new Static3D(+0.5f*(X-1),+0.5f*(Y-1),-0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(+0.5f*X,-0.5f*Y,+0.5f*Z);
-    center = new Static3D(+0.5f*(X-1),-0.5f*(Y-1),+0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(+0.5f*X,-0.5f*Y,-0.5f*Z);
-    center = new Static3D(+0.5f*(X-1),-0.5f*(Y-1),-0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(-0.5f*X,+0.5f*Y,+0.5f*Z);
-    center = new Static3D(-0.5f*(X-1),+0.5f*(Y-1),+0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(-0.5f*X,+0.5f*Y,-0.5f*Z);
-    center = new Static3D(-0.5f*(X-1),+0.5f*(Y-1),-0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(-0.5f*X,-0.5f*Y,+0.5f*Z);
-    center = new Static3D(-0.5f*(X-1),-0.5f*(Y-1),+0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-    vertices[0] = new Static3D(-0.5f*X,-0.5f*Y,-0.5f*Z);
-    center = new Static3D(-0.5f*(X-1),-0.5f*(Y-1),-0.5f*(Z-1));
-    roundCorners(mesh, center, vertices, strength, radius);
-
-  //  mesh.mergeEffComponents();
-
     return mesh;
     }
   }
diff --git a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
index a463f2a..7dc0492 100644
--- a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
+++ b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
@@ -330,16 +330,37 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
 
     private void createMesh()
       {
-      int type = 0;
-      int numLayers = 3;
-      float tmp   = (numLayers/3.0f)/(numLayers-1);
-      float height= tmp*COS18;
-      float width = tmp + type*height*SIN18/COS18;
+      final float[][] vertices = new float[][]
+          {
+              { 0.5f, 0.5f, 0.5f },
+              { 0.5f, 0.5f,-0.5f },
+              { 0.5f,-0.5f, 0.5f },
+              { 0.5f,-0.5f,-0.5f },
+              {-0.5f, 0.5f, 0.5f },
+              {-0.5f, 0.5f,-0.5f },
+              {-0.5f,-0.5f, 0.5f },
+              {-0.5f,-0.5f,-0.5f },
+          };
+
+      final int[][] vertIndexes = new int[][]
+          {
+              {0,1,3,2},
+              {5,4,6,7},
+              {5,1,0,4},
+              {6,2,3,7},
+              {4,0,2,6},
+              {1,5,7,3}
+          };
+
+      final float[][] bands = new float[][]
+          {
+              {0.048f,35,0.5f,0.7f,5,  2,2}
+          };
 
-      FactoryCubit factory = FactoryCubit.getInstance();
-      mMesh = factory.createKilominxEdgeMesh(numLayers, width, height, (type%2)==0 );
+      final int[] bandIndexes = new int[] { 0,0,0,0,0,0 };
 
-      //mMesh = createStaticMesh();
+      FactoryCubit factory = FactoryCubit.getInstance();
+      mMesh = factory.createRoundedSolid(vertices, vertIndexes, bands, bandIndexes);
 
       int numEff = mMesh.numEffComponents();
 
