commit 1bb5d3b779972043679dc503e0479fb57c54bc07
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu Oct 28 13:06:02 2021 +0200

    Add testing of MeshPolygon to the MeshFile app.

diff --git a/build.gradle b/build.gradle
index 465f18d..b06d21f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,4 +24,5 @@ dependencies {
     implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     implementation 'androidx.legacy:legacy-support-v13:1.0.0'
     implementation 'com.google.android.material:material:1.4.0'
+    implementation project(path: ':distorted-objectlib')
 }
diff --git a/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java b/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java
deleted file mode 100644
index 1ccfc93..0000000
--- a/src/main/java/org/distorted/examples/meshfile/FactoryCubit.java
+++ /dev/null
@@ -1,937 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Magic Cube.                                                              //
-//                                                                                               //
-// Magic Cube is free software: you can redistribute it and/or modify                            //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Magic Cube is distributed in the hope that it will be useful,                                 //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.meshfile;
-
-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.mesh.MeshBase;
-import org.distorted.library.mesh.MeshJoined;
-import org.distorted.library.mesh.MeshPolygon;
-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 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 final Static1D RADIUS = new Static1D(1);
-
-  private static FactoryCubit mThis;
-
-  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> mNewFaceTransf = new ArrayList<>();
-  private static final ArrayList<FaceTransform> mOldFaceTransf = new ArrayList<>();
-  private static final ArrayList<StickerCoords> mStickerCoords = new ArrayList<>();
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private FactoryCubit()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static FactoryCubit getInstance()
-    {
-    if( mThis==null ) mThis = new FactoryCubit();
-
-    return mThis;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// H - height of the band in the middle
-// alpha - angle of the edge  [0,90]
-// dist - often in a polygon the distance from edge to center is not 1, but something else.
-// This is the distance.
-// K - where to begin the second, much more flat part of the band. [0,1]
-// N - number of bands. N>=3
-//
-// theory: two distinct parts to the band:
-// 1) (0,B) - steep
-// 2) (B,1) - flat
-//
-// In first part, we have y = g(x) ; in second - y = g(f(x)) where
-//
-// g(x) = sqrt( R^2 - (x-D)^2 ) - R*cos(alpha)
-// f(x) = ((D-B)/(1-B)*x + B*(1-D)/(1-B)
-// h(x) = R*(sin(alpha) - sin(x))
-// R = H/(1-cos(alpha))
-// D = H*sin(alpha)
-// B = h(K*alpha)
-//
-// The N points are taken at:
-//
-// 1) in the second part, there are K2 = (N-3)/3 such points
-// 2) in the first - K1 = (N-3) - K2
-// 3) also, the 3 points 0,B,1
-//
-// so we have the sequence A[i] of N points
-//
-// 0
-// h((i+1)*(1-K)*alpha/(K1+1)) (i=0,1,...,K1-1)
-// B
-// (1-B)*(i+1)/(K2+1) + B   (i=0,i,...,K2-1)
-// 1
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float f(float D, float B, float x)
-    {
-    return ((D-B)*x + B*(1-D))/(1-B);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float g(float R, float D, float x, float cosAlpha)
-    {
-    float d = x-D;
-    return (float)(Math.sqrt(R*R-d*d)-R*cosAlpha);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float h(float R, float sinAlpha, float x)
-    {
-    return R*(sinAlpha-(float)Math.sin(x));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  float[] computeBands(float H, int alpha, float dist, float K, int N)
-    {
-    float[] bands = new float[2*N];
-
-    bands[0] = 1.0f;
-    bands[1] = 0.0f;
-
-    float beta = (float)Math.atan(dist*Math.tan(Math.PI*alpha/180));
-    float sinBeta = (float)Math.sin(beta);
-    float cosBeta = (float)Math.cos(beta);
-    float R = cosBeta<1.0f ? H/(1.0f-cosBeta) : 0.0f;
-    float D = R*sinBeta;
-    float B = h(R,sinBeta,K*beta);
-
-    if( D>1.0f )
-      {
-      for(int i=1; i<N; i++)
-        {
-        bands[2*i  ] = (float)(N-1-i)/(N-1);
-        bands[2*i+1] = H*(1-bands[2*i]);
-        }
-      }
-    else
-      {
-      int K2 = (int)((N-3)*K);
-      int K1 = (N-3)-K2;
-
-      for(int i=0; i<=K1; i++)
-        {
-        float angle = K*beta + (1-K)*beta*(K1-i)/(K1+1);
-        float x = h(R,sinBeta,angle);
-        bands[2*i+2] = 1.0f - x;
-        bands[2*i+3] = g(R,D,x,cosBeta);
-        }
-
-      for(int i=0; i<=K2; i++)
-        {
-        float x = (1-B)*(i+1)/(K2+1) + B;
-        bands[2*K1+2 + 2*i+2] = 1.0f - x;
-        bands[2*K1+2 + 2*i+3] = g(R,D,f(D,B,x),cosBeta);
-        }
-      }
-
-    bands[2*N-2] = 0.0f;
-    bands[2*N-1] =    H;
-
-    return bands;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void roundCorners(MeshBase mesh, Static3D center, Static3D[] vertices, float strength, float regionRadius)
-    {
-    Static4D region= new Static4D(0,0,0,regionRadius);
-
-    float centX = center.get0();
-    float centY = center.get1();
-    float centZ = center.get2();
-
-    for (Static3D vertex : vertices)
-      {
-      float x = strength*(centX - vertex.get0());
-      float y = strength*(centY - vertex.get1());
-      float z = strength*(centZ - vertex.get2());
-
-      VertexEffect effect = new VertexEffectDeform(new Static3D(x,y,z), RADIUS, vertex, region);
-      mesh.apply(effect);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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 FaceTransform constructNewTransform(final double[][] vert3D)
-    {
-    FaceTransform ft = new FaceTransform();
-
-    // compute center of gravity
-    ft.vx = 0.0f;
-    ft.vy = 0.0f;
-    ft.vz = 0.0f;
-    int len = vert3D.length;
-
-    for (double[] vert : vert3D)
-      {
-      ft.vx += vert[0];
-      ft.vy += vert[1];
-      ft.vz += vert[2];
-      }
-
-    ft.vx /= len;
-    ft.vy /= len;
-    ft.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] -= ft.vx;
-      vert3D[i][1] -= ft.vy;
-      vert3D[i][2] -= ft.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(ft, vert3D);
-
-    // remember the rotation
-    ft.qx =-mQuat1[0];
-    ft.qy =-mQuat1[1];
-    ft.qz =-mQuat1[2];
-    ft.qw = mQuat1[3];
-
-    return ft;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  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, int v1i, int v2i)
-    {
-    double v1x = v1[2*v1i];
-    double v1y = v1[2*v1i+1];
-    double v2x = v2[2*v2i];
-    double v2y = v2[2*v2i+1];
-
-    double lenSq1 = v1x*v1x + v1y*v1y;
-    double lenSq2 = v2x*v2x + v2y*v2y;
-
-    return Math.sqrt(lenSq2/lenSq1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// valid for 0<angle<2*PI
-
-  private double computeSinHalf(double cos)
-    {
-    return Math.sqrt((1-cos)/2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// valid for 0<angle<2*PI
-
-  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)
-    {
-    int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
-    double EPSILON = 0.001;
-    double scale = computeScale(newVert,oldVert,newZeroIndex,0);
-
-    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 correctTransform(FaceTransform ft, double scale, double sin, double cos, int oldSticker, boolean flip)
-    {
-    mStickerCoords.remove(ft.sticker);
-
-    ft.flip    = flip;
-    ft.sticker = oldSticker;
-    ft.scale  *= scale;
-
-    mQuat1[0] = ft.qx;
-    mQuat1[1] = ft.qy;
-    mQuat1[2] = ft.qz;
-    mQuat1[3] = ft.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 );
-
-    ft.qx = mQuat3[0];
-    ft.qy = mQuat3[1];
-    ft.qz = mQuat3[2];
-    ft.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 ft, 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) )
-        {
-        int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
-        double scale = computeScale(oldVert,newVert,0,newZeroIndex);
-        correctTransform(ft,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,
-                                      float[][] corners, int[] cornerIndexes,
-                                      float[][] centers, int[] centerIndexes )
-    {
-    int lenV = vertices.length;
-    Static3D[] staticVert = new Static3D[1];
-    Static3D center = new Static3D(0,0,0);
-
-    for(int v=0; v<lenV; v++)
-      {
-      staticVert[0] = new Static3D( (float)vertices[v][0], (float)vertices[v][1], (float)vertices[v][2]);
-
-      int cent = centerIndexes[v];
-
-      if( cent>=0 )
-        {
-        center.set( centers[cent][0], centers[cent][1], centers[cent][2]);
-
-        int corn = cornerIndexes[v];
-
-        if( corn>=0 )
-          {
-          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();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void printTransform(FaceTransform f)
-    {
-    android.util.Log.e("D", "q=("+f.qx+", "+f.qy+", "+f.qz+", "+f.qw+") v=("
-                       +f.vx+", "+f.vy+", "+f.vz+") scale="+f.scale+" sticker="+f.sticker);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 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", "---- OLD FACE TRANSFORM ---");
-
-    int oldfaces = mOldFaceTransf.size();
-
-    for(int f=0; f<oldfaces; f++)
-      {
-      printTransform(mOldFaceTransf.get(f));
-      }
-
-    android.util.Log.d("D", "---- NEW FACE TRANSFORM ---");
-
-    int newfaces = mNewFaceTransf.size();
-
-    for(int f=0; f<newfaces; f++)
-      {
-      printTransform(mNewFaceTransf.get(f));
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void clear()
-    {
-    mStickerCoords.clear();
-    mNewFaceTransf.clear();
-    mOldFaceTransf.clear();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void createNewFaceTransform( final double[][] vertices, final int[][] indexes)
-    {
-    FaceTransform ft;
-    int numNew = mNewFaceTransf.size();
-
-    for(int i=0; i<numNew; i++)
-      {
-      ft = mNewFaceTransf.remove(0);
-      mOldFaceTransf.add(ft);
-      }
-
-    int numFaces = indexes.length;
-    int numOld = mOldFaceTransf.size();
-
-    for (int face=0; face<numFaces; face++)
-      {
-      boolean collapsed = false;
-
-      double[][] vert = constructVert(vertices, indexes[face]);
-
-      FaceTransform newT = constructNewTransform(vert);
-
-      for (int old=0; !collapsed && old<numOld; old++)
-        {
-        ft = mOldFaceTransf.get(old);
-        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
-        }
-
-      for (int pre=0; !collapsed && pre<face; pre++)
-        {
-        ft = mNewFaceTransf.get(pre);
-        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
-        }
-
-      mNewFaceTransf.add(newT);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void computeConvexityCenter(double[] out, float[] in, FaceTransform ft)
-    {
-    if( in==null )
-      {
-      out[0] = out[1] = 0.0f;
-      }
-    else
-      {
-      out[0] = in[0] - ft.vx;
-      out[1] = in[1] - ft.vy;
-      out[2] = in[2] - ft.vz;
-      out[3] = 1.0f;
-
-      mQuat1[0] =-ft.qx;
-      mQuat1[1] =-ft.qy;
-      mQuat1[2] =-ft.qz;
-      mQuat1[3] = ft.qw;
-
-      mQuat2[0] = -mQuat1[0];
-      mQuat2[1] = -mQuat1[1];
-      mQuat2[2] = -mQuat1[2];
-      mQuat2[3] = +mQuat1[3];
-
-      quatMultiply(mQuat1, out  , mQuat3);
-      quatMultiply(mQuat3, mQuat2, out  );
-
-      out[0] /= ft.scale;
-      out[1] /= ft.scale;
-      out[2] /= ft.scale;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public MeshBase createRoundedSolid(final double[][] vertices, final int[][] vertIndexes,
-                                     final float[][] bands    , final int[]   bandIndexes,
-                                     final float[][] corners  , final int[]   cornerIndexes,
-                                     final float[][] centers  , final int[]   centerIndexes,
-                                     final int numComponents  , final float[] convexityCenter )
-    {
-    int numFaces = vertIndexes.length;
-    float[] band, bandsComputed;
-    MeshBase[] meshes = new MeshBase[numFaces];
-    FaceTransform fInfo;
-    StickerCoords sInfo;
-    double[] convexXY = new double[4];
-
-    for(int face=0; face<numFaces; face++)
-      {
-      fInfo = mNewFaceTransf.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];
-
-      computeConvexityCenter(convexXY,convexityCenter,fInfo);
-
-      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], (float)convexXY[0], (float)convexXY[1]);
-      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 = mNewFaceTransf.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, corners, cornerIndexes, centers, centerIndexes);
-
-    correctComponents(mesh,numComponents);
-
-    return mesh;
-    }
-  }
diff --git a/src/main/java/org/distorted/examples/meshfile/MeshFileActivity.java b/src/main/java/org/distorted/examples/meshfile/MeshFileActivity.java
index 84aa7ec..109ee8f 100644
--- a/src/main/java/org/distorted/examples/meshfile/MeshFileActivity.java
+++ b/src/main/java/org/distorted/examples/meshfile/MeshFileActivity.java
@@ -42,10 +42,12 @@ public class MeshFileActivity extends Activity implements AdapterView.OnItemSele
                                                           SeekBar.OnSeekBarChangeListener
 {
     final static int PROCEDURAL = -1;
+    final static int POLYGON    = -2;
 
     private LinearLayout mLayout;
     private int mResource;
     private String[] mNames = new String[] { "procedural" ,
+                                             "polygon"    ,
                                              "deferredjob",
                                              "meshjoin"   ,
                                              "predeform"  ,
@@ -186,18 +188,19 @@ public class MeshFileActivity extends Activity implements AdapterView.OnItemSele
         switch(pos)
           {
           case  0: mResource = PROCEDURAL       ; break;
-          case  1: mResource = R.raw.deferredjob; break;
-          case  2: mResource = R.raw.meshjoin   ; break;
-          case  3: mResource = R.raw.predeform  ; break;
-          case  4: mResource = R.raw.cube2      ; break;
-          case  5: mResource = R.raw.cube3      ; break;
-          case  6: mResource = R.raw.cube4      ; break;
-          case  7: mResource = R.raw.cube5      ; break;
-          case  8: mResource = R.raw.pyra3      ; break;
-          case  9: mResource = R.raw.pyra4      ; break;
-          case 10: mResource = R.raw.pyra5      ; break;
-          case 11: mResource = R.raw.dino       ; break;
-          case 12: mResource = R.raw.skewb      ; break;
+          case  1: mResource = POLYGON          ; break;
+          case  2: mResource = R.raw.deferredjob; break;
+          case  3: mResource = R.raw.meshjoin   ; break;
+          case  4: mResource = R.raw.predeform  ; break;
+          case  5: mResource = R.raw.cube2      ; break;
+          case  6: mResource = R.raw.cube3      ; break;
+          case  7: mResource = R.raw.cube4      ; break;
+          case  8: mResource = R.raw.cube5      ; break;
+          case  9: mResource = R.raw.pyra3      ; break;
+          case 10: mResource = R.raw.pyra4      ; break;
+          case 11: mResource = R.raw.pyra5      ; break;
+          case 12: mResource = R.raw.dino       ; break;
+          case 13: mResource = R.raw.skewb      ; break;
           }
         }
       }
diff --git a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
index 5147ea8..8f3dad7 100644
--- a/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
+++ b/src/main/java/org/distorted/examples/meshfile/MeshFileRenderer.java
@@ -38,10 +38,13 @@ import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.main.DistortedTexture;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.mesh.MeshFile;
+import org.distorted.library.mesh.MeshPolygon;
 import org.distorted.library.type.DynamicQuat;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 
+import org.distorted.objectlib.helpers.FactoryCubit;
+
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -49,6 +52,7 @@ import java.io.InputStream;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
+import static org.distorted.examples.meshfile.MeshFileActivity.POLYGON;
 import static org.distorted.examples.meshfile.MeshFileActivity.PROCEDURAL;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -172,13 +176,17 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
 
       long t1 = System.currentTimeMillis();
 
-      if( resourceID!=PROCEDURAL )
+      if( resourceID==PROCEDURAL )
         {
-        openMesh(resourceID);
+        createMesh();
+        }
+      else if( resourceID==POLYGON )
+        {
+        createPolygon();
         }
       else
         {
-        createMesh();
+        openMesh(resourceID);
         }
 
       long t2 = System.currentTimeMillis();
@@ -199,13 +207,13 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
       int[] colors;
       float F = 0.5f;
       float E = SQ3/2;
-      float G = SQ2/4;
 
       switch(resourceID)
           {
           case  R.raw.deferredjob:
           case  R.raw.meshjoin   :
           case  PROCEDURAL       :
+          case  POLYGON          :
           case  R.raw.predeform  : return createWhiteTexture();
 
           case  R.raw.cube2      :
@@ -250,6 +258,31 @@ class MeshFileRenderer implements GLSurfaceView.Renderer, DistortedLibrary.Excep
       return bitmap;
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void createPolygon()
+      {
+      float A = 0.5f;
+      float B = 0.1f;
+
+      int extraIndex    = 1;
+      int extraVertices = 2;
+      int numBands      = 3;
+
+      float[] vertices = new float[] { -A,-A, A,-A, A,A, -A,A };
+      float[] bands = new float[2*numBands];
+
+      for(int i=0; i<numBands; i++)
+        {
+        bands[2*i  ] = 1 + i/(1.0f-numBands);
+        bands[2*i+1] = B/(numBands-1)*i;
+        }
+
+      mMesh = new MeshPolygon(vertices,bands,extraIndex,extraVertices);
+      mMesh.setEffectAssociation(0,0,0);
+      mMesh.setShowNormals(true);
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     private void createMesh()
