commit 76a81b6a2f23b4089bd90c4c3c69b042aa1ea6cc
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Sun Aug 21 20:54:35 2016 +0100

    Combine Fragment3D and Vertex3D apps into one.

diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index e781ec3..c173c72 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -35,8 +35,7 @@
         <activity android:name=".cubes.CubesActivity" />       
         <activity android:name=".quaternion.QuaternionActivity" />          
         <activity android:name=".matrix3d.Matrix3DActivity" />
-        <activity android:name=".vertex3d.Vertex3DActivity" />
-        <activity android:name=".fragment3d.Fragment3DActivity" />
+        <activity android:name=".effects3d.Effects3DActivity" />
         <activity android:name=".plainmonalisa.PlainMonaLisaActivity" />
         <activity android:name=".save.SaveActivity"/>
     </application>
diff --git a/src/main/java/org/distorted/examples/TableOfContents.java b/src/main/java/org/distorted/examples/TableOfContents.java
index 36313aa..b5fc813 100644
--- a/src/main/java/org/distorted/examples/TableOfContents.java
+++ b/src/main/java/org/distorted/examples/TableOfContents.java
@@ -54,8 +54,7 @@ import org.distorted.examples.starwars.StarWarsActivity;
 import org.distorted.examples.cubes.CubesActivity;
 import org.distorted.examples.quaternion.QuaternionActivity;
 import org.distorted.examples.matrix3d.Matrix3DActivity;
-import org.distorted.examples.vertex3d.Vertex3DActivity;
-import org.distorted.examples.fragment3d.Fragment3DActivity;
+import org.distorted.examples.effects3d.Effects3DActivity;
 import org.distorted.examples.plainmonalisa.PlainMonaLisaActivity;
 import org.distorted.examples.save.SaveActivity;
 
@@ -262,20 +261,11 @@ public class TableOfContents extends ListActivity
 
    {
       final Map<String, Object> item = new HashMap<>();
-      item.put(ITEM_IMAGE, R.drawable.icon_example_vertex3d);
-      item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_vertex3d));
-      item.put(ITEM_SUBTITLE, getText(R.string.example_vertex3d_subtitle));
+      item.put(ITEM_IMAGE, R.drawable.icon_example_effects3d);
+      item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_effects3d));
+      item.put(ITEM_SUBTITLE, getText(R.string.example_effects3d_subtitle));
       data.add(item);
-      activityMapping.put(i++, Vertex3DActivity.class);
-   }
-
-   {
-      final Map<String, Object> item = new HashMap<>();
-      item.put(ITEM_IMAGE, R.drawable.icon_example_fragment3d);
-      item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_fragment3d));
-      item.put(ITEM_SUBTITLE, getText(R.string.example_fragment3d_subtitle));
-      data.add(item);
-      activityMapping.put(i++, Fragment3DActivity.class);
+      activityMapping.put(i++, Effects3DActivity.class);
    }
 
    {
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java b/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java
new file mode 100644
index 0000000..04412bc
--- /dev/null
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java
@@ -0,0 +1,580 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.effects3d;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+import android.widget.Spinner;
+import android.widget.TableRow;
+
+import org.distorted.examples.R;
+import org.distorted.library.Distorted;
+import org.distorted.library.DistortedBitmap;
+import org.distorted.library.DistortedCubes;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectNames;
+import org.distorted.library.EffectTypes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class Effects3DActivity extends Activity
+                              implements View.OnClickListener,
+                                         AdapterView.OnItemSelectedListener
+  {
+  private static final int COLOR_OFF = 0xffffe81f;
+  private static final int COLOR_ON  = 0xff0000ff;
+
+  private boolean firstScreen;
+
+  // fields needed for the first 'pick-a-shape' screen
+  //
+  private int mNumCols = 10;
+  private int mNumRows = 10;
+  private NumberPicker mColsPicker, mRowsPicker;
+  private boolean[] mShape;
+  private DistortedObject mObject;
+  private int mObjectType;
+  private int mBitmap;
+
+  private ArrayList<Effects3DEffect> mEffects;
+  private int mEffectAdd;
+  private float mCenterX, mCenterY;
+  private float mRegionX, mRegionY, mRegionR;
+
+  private EffectNames[] mEffectNames;
+
+  private static boolean mSupportsRegion = true;
+  private static boolean mTypeVertex     = false;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  protected void onCreate(Bundle savedState)
+    {
+    super.onCreate(savedState);
+
+    mEffects = new ArrayList<>();
+
+    createEffectNames();
+
+    setContentView(R.layout.objectpickerlayout);
+
+    mColsPicker = (NumberPicker)findViewById(R.id.objectpicker_cols);
+    mRowsPicker = (NumberPicker)findViewById(R.id.objectpicker_rows);
+
+    mColsPicker.setMaxValue(15);
+    mColsPicker.setMinValue( 0);
+    mRowsPicker.setMaxValue(15);
+    mRowsPicker.setMinValue( 0);
+
+    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+      {
+      @Override
+      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+        {
+        mNumCols = mColsPicker.getValue();
+        }
+      });
+
+    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+      {
+      @Override
+      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+        {
+        mNumRows = mRowsPicker.getValue();
+        }
+      });
+
+    firstScreen = true;
+
+    mObjectType = 0;
+
+    Spinner typeSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerType);
+    typeSpinner.setOnItemSelectedListener(this);
+
+    String[] objectType = new String[] {"DistortedCubes", "DistortedBitmap"};
+
+    ArrayAdapter<String> adapterType = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectType);
+    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    typeSpinner.setAdapter(adapterType);
+
+    Spinner bitmapSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerBitmap);
+    bitmapSpinner.setOnItemSelectedListener(this);
+
+    String[] objectBitmap = new String[] { "Grid", "Girl", "Dog", "Cat", "Squares"};
+
+    ArrayAdapter<String> adapterBitmap = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectBitmap);
+    adapterBitmap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    bitmapSpinner.setAdapter(adapterBitmap);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createEffectNames()
+    {
+    EffectTypes type1 = EffectTypes.FRAGMENT;
+    EffectTypes type2 = EffectTypes.VERTEX;
+
+    EffectNames[] names = EffectNames.values();
+
+    int numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type1 || names[i].getType() == type2 ) numEffects++;
+
+    mEffectNames = new EffectNames[numEffects];
+
+    numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type1 || names[i].getType() == type2 )
+        {
+        mEffectNames[numEffects++] = names[i];
+        }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setGrid()
+    {
+    LinearLayout lay = (LinearLayout)findViewById(R.id.objectpicker_buttongrid);
+
+    int width = lay.getWidth();
+    int height= lay.getHeight();
+    int w = mNumCols>0 ? (width / mNumCols) -10 : 0;
+    int h = mNumRows>0 ? (height/ mNumRows) -10 : 0;
+    int size= w<h ? w:h;
+    int pad = size/20;
+
+    lay.removeAllViews();
+
+    mShape = new boolean[mNumRows*mNumCols];
+
+    TableRow.LayoutParams p = new TableRow.LayoutParams();
+
+    p.rightMargin  = pad;
+    p.leftMargin   = pad;
+    p.topMargin    = pad;
+    p.bottomMargin = pad;
+    p.height       = size;
+    p.width        = size;
+
+    for (int rows=0; rows<mNumRows; rows++)
+      {
+      TableRow tr = new TableRow(this);
+      tr.setGravity(Gravity.CENTER);
+
+      for(int cols=0; cols<mNumCols; cols++)
+        {
+        Button b = new Button(this);
+        b.setOnClickListener(this);
+        b.setId(rows*mNumCols+cols);
+        b.setLayoutParams(p);
+        b.setBackgroundColor(COLOR_ON);
+        tr.addView(b, p);
+        mShape[rows*mNumCols+cols] = true;
+        }
+
+      lay.addView(tr);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DistortedObject getObject()
+    {
+    return mObject;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void setSupportsRegion(boolean supports)
+    {
+    mSupportsRegion = supports;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static boolean supportsRegion()
+    {
+    return mSupportsRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static void setTypeVertex(boolean type)
+    {
+    mTypeVertex = type;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static boolean isTypeVertex()
+    {
+    return mTypeVertex;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Bitmap getBitmap()
+    {
+    Bitmap bmp;
+
+    if( mBitmap!=-1)
+      {
+      InputStream is = getResources().openRawResource(mBitmap);
+
+      try
+        {
+        bmp = BitmapFactory.decodeStream(is);
+        }
+      finally
+        {
+        try
+          {
+          is.close();
+          }
+        catch(IOException e) { }
+        }
+      }
+    else
+      {
+      final int W = 640;
+      final int H = 640;
+
+      Paint paint = new Paint();
+      bmp = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
+      Canvas canvas = new Canvas(bmp);
+
+      paint.setAntiAlias(true);
+      paint.setTextAlign(Paint.Align.CENTER);
+      paint.setColor(0xff008800);
+      paint.setStyle(Paint.Style.FILL);
+      canvas.drawRect(0, 0, W, H, paint);
+      paint.setColor(0xffffffff);
+
+      for(int i=0; i<=mNumCols ; i++ )
+        {
+        canvas.drawRect(W*i/mNumCols - 1,                0,  W*i/mNumCols + 1,  H               , paint);
+        canvas.drawRect(               0, H *i/mNumCols -1,  W               ,  H*i/mNumCols + 1, paint);
+        }
+      }
+
+    return bmp;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onClick(View view)
+    {
+    Button tmp = (Button)view;
+    int id = tmp.getId();
+    mShape[id] = !mShape[id];
+    tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setRegion(float x, float y, float r)
+    {
+    mRegionX = x;
+    mRegionY =-y;
+    mRegionR = r;
+
+    Effects3DSurfaceView view = (Effects3DSurfaceView)findViewById(R.id.effects3dSurfaceView);
+
+    view.getRenderer().setRegion(mRegionX, mRegionY, mRegionR);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setCenter(float x, float y)
+    {
+    mCenterX = x;
+    mCenterY = y;
+
+    Effects3DSurfaceView view = (Effects3DSurfaceView)findViewById(R.id.effects3dSurfaceView);
+
+    view.getRenderer().setCenter( mCenterX, mCenterY );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterX()
+    {
+    return mCenterX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterY()
+    {
+    return mCenterY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionX()
+    {
+    return mRegionX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionY()
+    {
+    return mRegionY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionR()
+    {
+    return mRegionR;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void Continue(View v)
+    {
+    firstScreen = false;
+
+    if( mObjectType==1 )
+      {
+      mObject = new DistortedBitmap(100,100,mNumCols);
+      setEffectView();
+      }
+    else
+      {
+      View view = getLayoutInflater().inflate(R.layout.objectpicker2layout, null);
+
+      setContentView(view);
+
+      view.post(new Runnable() {
+            @Override
+            public void run() {
+              setGrid();
+            }
+        });
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void Create(View v)
+    {
+    firstScreen = false;
+
+    String str = "";
+
+    for(int i=0; i<mNumRows*mNumCols; i++)
+      str += mShape[i] ? "1" : "0";
+
+    mObject = new DistortedCubes(mNumCols, str, 10);
+
+    setEffectView();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    switch(parent.getId())
+      {
+      case R.id.objectpicker_spinnerType  : mObjectType = pos;
+                                            break;
+      case R.id.objectpicker_spinnerBitmap: switch(pos)
+                                              {
+                                              case 0: mBitmap = -1        ; break;
+                                              case 1: mBitmap = R.raw.face; break;
+                                              case 2: mBitmap = R.raw.dog;  break;
+                                              case 3: mBitmap = R.raw.cat;  break;
+                                              case 4: mBitmap = R.raw.grid; break;
+                                              }
+                                            break;
+      case R.id.effects3dspinner          : mEffectAdd = pos;
+                                            break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent)
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getWidth()
+    {
+    return mObject==null ? 0: mObject.getWidth();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getHeight()
+    {
+    return mObject==null ? 0: mObject.getHeight();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void resetData()
+    {
+    mCenterX = 0.5f*getWidth();
+    mCenterY = 0.5f*getHeight();
+    mRegionX = 0;
+    mRegionY = 0;
+    mRegionR = getWidth()/2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 'second screen' methods
+
+  private void setEffectView()
+    {
+    resetData();
+
+    final View view = getLayoutInflater().inflate(R.layout.effects3dlayout, null);
+
+    setContentView(view);
+
+    String[] effects = new String[mEffectNames.length];
+
+    for(int i=0; i<mEffectNames.length; i++) effects[i] = mEffectNames[i].name();
+
+    Spinner effectSpinner = (Spinner)findViewById(R.id.effects3dspinner );
+    effectSpinner.setOnItemSelectedListener(this);
+
+    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, effects);
+    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    effectSpinner.setAdapter(adapterEffect);
+
+    mEffectAdd = 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void newEffect(View v)
+    {
+    Effects3DEffect eff = new Effects3DEffect(mEffectNames[mEffectAdd], this);
+    mEffects.add(eff);
+
+    LinearLayout layout = (LinearLayout)findViewById(R.id.effects3dlayout);
+    View view = eff.createView();
+    layout.addView(view);
+
+    if( mTypeVertex )
+      {
+      View center = eff.createCenter();
+      layout.addView(center);
+      }
+
+    if( mSupportsRegion )
+      {
+      View region = eff.createRegion();
+      layout.addView(region);
+      }
+
+    eff.apply(mObject);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void removeAll(View v)
+    {
+    mEffects.clear();
+    LinearLayout layout = (LinearLayout)findViewById(R.id.effects3dlayout);
+    layout.removeAllViews();
+    mObject.abortEffects(EffectTypes.VERTEX);
+    mObject.abortEffects(EffectTypes.FRAGMENT);
+
+    resetData();
+
+    Effects3DSurfaceView view = (Effects3DSurfaceView)findViewById(R.id.effects3dSurfaceView);
+    Effects3DRenderer renderer= view.getRenderer();
+
+    renderer.setCenter( mCenterX, mCenterY );
+    renderer.setRegion( mRegionX, mRegionY, mRegionR );
+    renderer.mQuat1.set(0,0,0,1);
+    renderer.mQuat2.set(0,0,0,1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Overrides
+
+  @Override
+  protected void onPause()
+    {
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.effects3dSurfaceView);
+    if( mView!=null ) mView.onPause();
+    super.onPause();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onResume()
+    {
+    super.onResume();
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.effects3dSurfaceView);
+    if( mView!=null ) mView.onResume();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  public void onWindowFocusChanged(boolean hasFocus)
+    {
+    super.onWindowFocusChanged(hasFocus);
+
+    if( firstScreen )
+      {
+      mColsPicker.setValue(mNumCols);
+      mRowsPicker.setValue(mNumRows);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onDestroy()
+    {
+    Distorted.onDestroy();
+    super.onDestroy();
+    }
+
+  }
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java b/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java
new file mode 100644
index 0000000..ccf3c92
--- /dev/null
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java
@@ -0,0 +1,548 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.effects3d;
+
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.distorted.examples.R;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectNames;
+import org.distorted.library.EffectTypes;
+import org.distorted.library.type.Dynamic1D;
+import org.distorted.library.type.Dynamic2D;
+import org.distorted.library.type.Dynamic3D;
+import org.distorted.library.type.Dynamic4D;
+import org.distorted.library.type.Static1D;
+import org.distorted.library.type.Static2D;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+
+import java.lang.ref.WeakReference;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class Effects3DEffect implements SeekBar.OnSeekBarChangeListener
+  {
+  private WeakReference<Effects3DActivity> mAct;
+
+  private EffectNames mName;
+  private int[] mInter;
+  private int[] mInterRegion;
+  private int[] mInterCenter;
+  private int[] mSeekID;
+  private int[] mSeekRegionID;
+  private int[] mSeekCenterID;
+  private int mDimension;
+  private TextView mText,mTextRegion,mTextCenter;
+
+  private Dynamic1D mDyn1;
+  private Dynamic3D mDyn3;
+  private Static1D  mSta1;
+  private Static3D  mSta3;
+  private Dynamic4D mRegionDyn;
+  private Static4D  mRegionSta;
+  private Dynamic2D mCenterDyn;
+  private Static2D  mCenterSta;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private int getDimension()
+    {
+    switch(mName)
+      {
+      case DISTORT      :
+      case DEFORM       : return 3;
+
+      case CHROMA       :
+      case SMOOTH_CHROMA: return 4;
+
+      default           : return 1;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  public void apply(DistortedObject object)
+    {
+    switch(mName)
+      {
+      case DISTORT: object.distort(mDyn3, mCenterDyn, mRegionDyn); break;
+      case DEFORM : object.deform (mDyn3, mCenterDyn            ); break;
+      case SINK   : object.sink   (mDyn1, mCenterDyn, mRegionDyn); break;
+      case SWIRL  : object.swirl  (mDyn1, mCenterDyn, mRegionDyn); break;
+
+      case ALPHA            : object.alpha     (mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_ALPHA     : object.alpha     (mDyn1,        mRegionDyn, true ); break;
+      case CHROMA           : object.chroma    (mDyn1, mDyn3, mRegionDyn, false); break;
+      case SMOOTH_CHROMA    : object.chroma    (mDyn1, mDyn3, mRegionDyn, true ); break;
+      case BRIGHTNESS       : object.brightness(mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_BRIGHTNESS: object.brightness(mDyn1,        mRegionDyn, true ); break;
+      case SATURATION       : object.saturation(mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_SATURATION: object.saturation(mDyn1,        mRegionDyn, true ); break;
+      case CONTRAST         : object.contrast  (mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_CONTRAST  : object.contrast  (mDyn1,        mRegionDyn, true ); break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private boolean supportsRegion()
+    {
+    return mName == EffectNames.DEFORM ? false : true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void fillStatics()
+    {
+    switch(mName)
+      {
+      case DISTORT          :
+      case DEFORM           : float l = mAct.get().getWidth()/50.0f;
+                              float x = (mInter[0]-50)*l;
+                              float y = (mInter[1]-50)*l;
+                              float z = (mInter[2]-50)*l;
+                              mSta3.set(x,y,z);
+                              break;
+      case SINK             : mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
+                              break;
+      case SWIRL            : mSta1.set( 3.6f*(mInter[0]-50) );
+                              break;
+
+      case ALPHA            :
+      case SMOOTH_ALPHA     :
+      case BRIGHTNESS       :
+      case SMOOTH_BRIGHTNESS:
+      case SATURATION       :
+      case SMOOTH_SATURATION:
+      case CONTRAST         :
+      case SMOOTH_CONTRAST  : mSta1.set(mInter[0]/100.0f);
+                              break;
+
+      case CHROMA           :
+      case SMOOTH_CHROMA    : mSta1.set(mInter[0]/100.0f);
+                              mSta3.set(255.0f*mInter[1]/100,
+                                        255.0f*mInter[2]/100,
+                                        255.0f*mInter[3]/100);
+                              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void setDefaultInter()
+    {
+    switch(mName)
+      {
+      case DISTORT          :
+      case DEFORM           : mInter[0] = 50;
+                              mInter[1] = 50;
+                              mInter[2] = 50;
+                              break;
+      case SINK             :
+      case SWIRL            : mInter[0] = 50;
+                              break;
+
+      case ALPHA            :
+      case SMOOTH_ALPHA     :
+      case BRIGHTNESS       :
+      case SMOOTH_BRIGHTNESS:
+      case SATURATION       :
+      case SMOOTH_SATURATION:
+      case CONTRAST         :
+      case SMOOTH_CONTRAST  : mInter[0] = 50;
+                              break;
+
+      case CHROMA           :
+      case SMOOTH_CHROMA    : mInter[0] = 50;
+                              mInter[1] =  0;
+                              mInter[2] =  0;
+                              mInter[3] =  0;
+                              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void setText()
+    {
+    String text = mName.name();
+
+    if( mName.getType()== EffectTypes.FRAGMENT )
+      {
+      text+=(" "+((int)(mSta1.getX()*100))/100.0f);
+      }
+
+    if( mName==EffectNames.DISTORT || mName==EffectNames.DEFORM       ||
+        mName==EffectNames.CHROMA  || mName==EffectNames.SMOOTH_CHROMA )
+      {
+      int f1 = (int)mSta3.getX();
+      int f2 = (int)mSta3.getY();
+      int f3 = (int)mSta3.getZ();
+      text += " ("+f1+","+f2+","+f3+")";
+      }
+    else if(mName.getType() == EffectTypes.VERTEX)
+      {
+      float f1 = ((int)(mSta1.getX()*100))/100.0f;
+      text += " ("+f1+")";
+      }
+
+    mText.setText(text);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillCenterStatics()
+    {
+    Effects3DActivity act = mAct.get();
+
+    float x = (mInterCenter[0]*0.012f - 0.1f)*act.getWidth();
+    float y = (mInterCenter[1]*0.012f - 0.1f)*act.getHeight();
+    mCenterSta.set(x,y);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultCenterInter()
+    {
+    mInterCenter[0] = 50;
+    mInterCenter[1] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setCenterText()
+    {
+    int f0 = (int)mCenterSta.getX();
+    int f1 = (int)mCenterSta.getY();
+
+    mTextCenter.setText("center ("+f0+","+f1+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillRegionStatics()
+    {
+    Effects3DActivity act = mAct.get();
+
+    float factorX = act.getWidth() / 100.0f;
+    float factorY = act.getHeight()/ 100.0f;
+
+    int deduct = (mName.getType() == EffectTypes.VERTEX ? 50:0);
+
+    float  x = (mInterRegion[0]-deduct)*factorX;
+    float  y = (mInterRegion[1]-deduct)*factorY;
+    float rx =  mInterRegion[2]        *factorX;
+    float ry =  mInterRegion[3]        *factorY;
+
+    mRegionSta.set(x,y,rx,ry);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultRegionInter()
+    {
+    mInterRegion[0] = 50;
+    mInterRegion[1] = 50;
+    mInterRegion[2] = 50;
+    mInterRegion[3] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setRegionText()
+    {
+    int f0 = (int)mRegionSta.getX();
+    int f1 = (int)mRegionSta.getY();
+    int f2 = (int)mRegionSta.getZ();
+    int f3 = (int)mRegionSta.getW();
+
+    mTextRegion.setText("region ("+f0+","+f1+","+f2+","+f3+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Effects3DEffect(EffectNames name, Effects3DActivity act)
+    {
+    mAct = new WeakReference<>(act);
+    mName = name;
+
+    mDimension = getDimension();
+
+    switch(mDimension)
+      {
+      case 1 : mDyn1 = new Dynamic1D();
+               mSta1 = new Static1D(0);
+               mDyn1.add(mSta1);
+               mDyn3 = null;
+               break;
+      case 3 : mDyn3 = new Dynamic3D();
+               mSta3 = new Static3D(0,0,0);
+               mDyn3.add(mSta3);
+               mDyn1 = null;
+               break;
+      case 4 : mDyn3 = new Dynamic3D();
+               mSta3 = new Static3D(0,0,0);
+               mDyn3.add(mSta3);
+               mDyn1 = new Dynamic1D();
+               mSta1 = new Static1D(0);
+               mDyn1.add(mSta1);
+               break;
+      default: throw new RuntimeException("unsupported effect");
+      }
+
+    mInter = new int[mDimension];
+    mSeekID= new int[mDimension];
+
+    mInterRegion = new int[4];
+    mSeekRegionID= new int[4];
+    mRegionDyn   = new Dynamic4D();
+    mRegionSta   = new Static4D(0,0,0,0);
+    mRegionDyn.add(mRegionSta);
+
+    mInterCenter = new int[2];
+    mSeekCenterID= new int[2];
+    mCenterDyn   = new Dynamic2D();
+    mCenterSta   = new Static2D(0,0);
+    mCenterDyn.add(mCenterSta);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View createView()
+    {
+    View effect;
+    SeekBar[] seek = new SeekBar[mDimension];
+
+    Effects3DActivity act = mAct.get();
+
+    switch(mDimension)
+      {
+      case 1 : effect     = act.getLayoutInflater().inflate(R.layout.effect1d, null);
+               mText      = (TextView)effect.findViewById(R.id.effect1dText);
+               seek[0]    = (SeekBar)effect.findViewById(R.id.effect1dbar1);
+               mSeekID[0] = seek[0].getId();
+               break;
+      case 2 : effect     = act.getLayoutInflater().inflate(R.layout.effect2d, null);
+               mText      = (TextView)effect.findViewById(R.id.effect2dText);
+               seek[0]    = (SeekBar)effect.findViewById(R.id.effect2dbar1);
+               seek[1]    = (SeekBar)effect.findViewById(R.id.effect2dbar2);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               break;
+      case 3 : effect     = act.getLayoutInflater().inflate(R.layout.effect3d, null);
+               mText      = (TextView)effect.findViewById(R.id.effect3dText);
+               seek[0]    = (SeekBar)effect.findViewById(R.id.effect3dbar1);
+               seek[1]    = (SeekBar)effect.findViewById(R.id.effect3dbar2);
+               seek[2]    = (SeekBar)effect.findViewById(R.id.effect3dbar3);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               break;
+      case 4 : effect     = act.getLayoutInflater().inflate(R.layout.effect4d, null);
+               mText      = (TextView)effect.findViewById(R.id.effect4dText);
+               seek[0]    = (SeekBar)effect.findViewById(R.id.effect4dbar1);
+               seek[1]    = (SeekBar)effect.findViewById(R.id.effect4dbar2);
+               seek[2]    = (SeekBar)effect.findViewById(R.id.effect4dbar3);
+               seek[3]    = (SeekBar)effect.findViewById(R.id.effect4dbar4);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mSeekID[3] = seek[3].getId();
+               break;
+      default: android.util.Log.e("Effects3DEffect", "dimension "+mDimension+" not supported!");
+               return null;
+      }
+
+    setDefaultInter();
+
+    for(int i=0; i<mDimension; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInter[i] );
+      }
+
+    Effects3DActivity.setSupportsRegion(supportsRegion());
+    Effects3DActivity.setTypeVertex(mName.getType() == EffectTypes.VERTEX);
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View createRegion()
+    {
+    Effects3DActivity act = mAct.get();
+
+    View region = act.getLayoutInflater().inflate(R.layout.effectregion, null);
+
+    SeekBar[] seek = new SeekBar[4];
+
+    seek[0] = (SeekBar)region.findViewById(R.id.effectRegionBarX );
+    seek[1] = (SeekBar)region.findViewById(R.id.effectRegionBarY );
+    seek[2] = (SeekBar)region.findViewById(R.id.effectRegionBarRX);
+    seek[3] = (SeekBar)region.findViewById(R.id.effectRegionBarRY);
+
+    mSeekRegionID[0] = seek[0].getId();
+    mSeekRegionID[1] = seek[1].getId();
+    mSeekRegionID[2] = seek[2].getId();
+    mSeekRegionID[3] = seek[3].getId();
+
+    mTextRegion = (TextView)region.findViewById(R.id.effectRegionText);
+
+    setDefaultRegionInter();
+
+    for(int i=0; i<4; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInterRegion[i] );
+      }
+
+    act.setRegion(mRegionSta.getX(),mRegionSta.getY(),mRegionSta.getZ());
+
+    return region;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View createCenter()
+    {
+    Effects3DActivity act = mAct.get();
+
+    View center = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
+
+    SeekBar[] seek = new SeekBar[2];
+
+    seek[0] = (SeekBar)center.findViewById(R.id.effectCenterBarX );
+    seek[1] = (SeekBar)center.findViewById(R.id.effectCenterBarY );
+
+    mSeekCenterID[0] = seek[0].getId();
+    mSeekCenterID[1] = seek[1].getId();
+
+    mTextCenter = (TextView)center.findViewById(R.id.effectCenterText);
+
+    setDefaultCenterInter();
+
+    for(int i=0; i<2; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInterCenter[i] );
+      }
+
+    act.setCenter(mCenterSta.getX(),mCenterSta.getY());
+
+    return center;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    if ( mDimension>=1 && bar.getId()==mSeekID[0] )
+      {
+      mInter[0] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=2 && bar.getId()==mSeekID[1] )
+      {
+      mInter[1] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=3 && bar.getId()==mSeekID[2] )
+      {
+      mInter[2] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=4 && bar.getId()==mSeekID[3] )
+      {
+      mInter[3] = progress;
+      fillStatics();
+      setText();
+      }
+
+    if( bar.getId() == mSeekRegionID[0] )
+      {
+      mInterRegion[0] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( bar.getId() == mSeekRegionID[1] )
+      {
+      mInterRegion[1] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( bar.getId() == mSeekRegionID[2] )
+      {
+      mInterRegion[2] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( bar.getId() == mSeekRegionID[3] )
+      {
+      mInterRegion[3] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+
+    if( bar.getId() == mSeekCenterID[0] )
+      {
+      mInterCenter[0] = progress;
+      fillCenterStatics();
+      setCenterText();
+      }
+    if( bar.getId() == mSeekCenterID[1] )
+      {
+      mInterCenter[1] = progress;
+      fillCenterStatics();
+      setCenterText();
+      }
+
+    if( fromUser )
+      {
+      Effects3DActivity.setSupportsRegion(supportsRegion());
+      Effects3DActivity.setTypeVertex(mName.getType() == EffectTypes.VERTEX);
+
+      Effects3DActivity act = mAct.get();
+
+      act.setCenter(mCenterSta.getX(),mCenterSta.getY());
+      act.setRegion(mRegionSta.getX(),mRegionSta.getY(),mRegionSta.getZ());
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStartTrackingTouch(SeekBar bar) { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java b/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java
new file mode 100644
index 0000000..c96ab24
--- /dev/null
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java
@@ -0,0 +1,250 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.effects3d;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import org.distorted.examples.R;
+import org.distorted.library.Distorted;
+import org.distorted.library.DistortedBitmap;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectTypes;
+import org.distorted.library.type.Dynamic3D;
+import org.distorted.library.type.DynamicQuat;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Effects3DRenderer implements GLSurfaceView.Renderer
+{
+    private GLSurfaceView mView;
+    private DistortedObject mObject;
+    private DistortedBitmap mBackground;
+    private DistortedBitmap mCenter;
+    private DistortedBitmap mRegion;
+    private int mObjWidth, mObjHeight, mObjDepth;
+    private DynamicQuat mQuatInt1, mQuatInt2;
+
+    private Dynamic3D mCenterInter, mRegionInter;
+    private Static3D mCenterPoint, mRegionPoint;
+    private Dynamic3D mRegionScaleInter;
+    private Static3D mRegionScalePoint;
+
+    private float mFactorObj, mFactorReg;
+
+    Static4D mQuat1, mQuat2;
+    int mScreenMin;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public Effects3DRenderer(GLSurfaceView v)
+      {
+      mView = v;
+
+      mObject     = ((Effects3DActivity)v.getContext()).getObject();
+      mBackground = new DistortedBitmap(100, 100, 1);
+      mCenter     = new DistortedBitmap(100, 100, 1);
+      mRegion     = new DistortedBitmap(100, 100, 1);
+
+      mObjWidth = mObject.getWidth();
+      mObjHeight= mObject.getHeight();
+      mObjDepth = mObject.getDepth();
+
+      mQuat1 = new Static4D(0,0,0,1);  // unity
+      mQuat2 = new Static4D(0,0,0,1);  // quaternions
+
+      mQuatInt1 = new DynamicQuat(0,0.5f);
+      mQuatInt2 = new DynamicQuat(0,0.5f);
+
+      mQuatInt1.add(mQuat1);
+      mQuatInt2.add(mQuat2);
+
+      mCenterInter= new Dynamic3D();
+      mCenterPoint= new Static3D(0,0,0);
+      mCenterInter.add(mCenterPoint);
+
+      mRegionInter= new Dynamic3D();
+      mRegionPoint= new Static3D(0,0,0);
+      mRegionInter.add(mRegionPoint);
+
+      mRegionScaleInter = new Dynamic3D();
+      mRegionScalePoint = new Static3D(0,0,0);
+      mRegionScaleInter.add(mRegionScalePoint);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setCenter(float x, float y)
+      {
+      mCenterPoint.set(mFactorObj*x,mFactorObj*y,0);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setRegion(float x, float y, float r)
+      {
+      mFactorReg = 2*mFactorObj*r/mRegion.getWidth();
+      mRegionPoint.set(mFactorObj*x,mFactorObj*y,0);
+      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onDrawFrame(GL10 glUnused)
+      {
+      GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+
+      long time = System.currentTimeMillis();
+
+      mBackground.draw(time);
+      mObject.draw(time);
+
+      if( Effects3DActivity.isTypeVertex() )
+        {
+        mCenter.draw(time);
+        if( Effects3DActivity.supportsRegion() ) mRegion.draw(time);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onSurfaceChanged(GL10 glUnused, int width, int height)
+      {
+      mScreenMin = width<height ? width:height;
+
+      mObject.abortEffects(EffectTypes.MATRIX);
+      mBackground.abortEffects(EffectTypes.MATRIX);
+      mCenter.abortEffects(EffectTypes.MATRIX);
+      mRegion.abortEffects(EffectTypes.MATRIX);
+
+      float factorCen;
+      int centerSize = mCenter.getWidth();
+      int regionSize = mRegion.getWidth();
+
+      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
+        {
+        mFactorObj = (0.80f*height)/mObjHeight;
+        factorCen  = (0.08f*height)/centerSize;
+        }
+      else
+        {
+        mFactorObj = (0.80f*width)/mObjWidth;
+        factorCen  = (0.08f*width)/centerSize;
+        }
+
+      Effects3DActivity act = (Effects3DActivity)mView.getContext();
+      mCenterPoint.set(mFactorObj*act.getCenterX(),mFactorObj*act.getCenterY(),0);
+      mRegionPoint.set(mFactorObj*act.getRegionX(),mFactorObj*act.getRegionY(),0);
+
+      mFactorReg = 2*mFactorObj*act.getRegionR()/regionSize;
+      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
+
+      Static3D rotateObj = new Static3D(mObjWidth/2,mObjHeight/2, 0);
+
+      mObject.move( new Static3D( (width-mFactorObj*mObjWidth)/2 , (height-mFactorObj*mObjHeight)/2 , 0) );
+      mObject.scale(mFactorObj);
+      mObject.quaternion(mQuatInt1, rotateObj);
+      mObject.quaternion(mQuatInt2, rotateObj);
+
+      Static3D rotateCen = new Static3D(width/2,height/2, 0);
+
+      mCenter.quaternion(mQuatInt1, rotateCen);
+      mCenter.quaternion(mQuatInt2, rotateCen);
+
+      mCenter.move( new Static3D( (width -factorCen*centerSize-mFactorObj*mObjWidth )/2 ,
+                                  (height-factorCen*centerSize-mFactorObj*mObjHeight)/2 , mFactorObj*mObjDepth/2+10) );
+      mCenter.move(mCenterInter);
+      mCenter.scale(factorCen);
+
+      mRegion.quaternion(mQuatInt1, rotateCen);
+      mRegion.quaternion(mQuatInt2, rotateCen);
+
+      mRegion.move( new Static3D( (width -mFactorObj*mObjWidth )/2 ,
+                                  (height-mFactorObj*mObjHeight)/2 , mFactorObj*mObjDepth/2+12) );
+      mRegion.move(mCenterInter);
+      mRegion.move(mRegionInter);
+      mRegion.scale(mRegionScaleInter);
+      mRegion.move( new Static3D( -regionSize/2 , -regionSize/2 , 0) );
+
+      int backgroundSize = mBackground.getWidth();
+      float factorBackX = ((float)width)/backgroundSize;
+      float factorBackY = ((float)height)/backgroundSize;
+
+      mBackground.move(new Static3D( -width/2, -height/2,-mFactorObj*(mObjWidth+mObjHeight)/2) );
+      mBackground.scale(new Static3D(2*factorBackX, 2*factorBackY, 1.0f) );
+
+      Distorted.onSurfaceChanged(width, height);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
+      {
+      Effects3DActivity act = (Effects3DActivity)mView.getContext();
+
+      InputStream is1 = act.getResources().openRawResource(R.raw.water);
+      InputStream is2 = act.getResources().openRawResource(R.raw.center);
+      InputStream is3 = act.getResources().openRawResource(R.raw.region);
+
+      Bitmap bitmap1,bitmap2,bitmap3;
+        
+      try 
+        {
+        bitmap1 = BitmapFactory.decodeStream(is1);
+        bitmap2 = BitmapFactory.decodeStream(is2);
+        bitmap3 = BitmapFactory.decodeStream(is3);
+        }
+      finally 
+        {
+        try 
+          {
+          is1.close();
+          is2.close();
+          is3.close();
+          }
+        catch(IOException e) { }
+        }  
+      
+      mObject.setBitmap( act.getBitmap() );
+      mBackground.setBitmap(bitmap1);
+      mCenter.setBitmap(bitmap2);
+      mRegion.setBitmap(bitmap3);
+
+      try
+        {
+        Distorted.onSurfaceCreated(mView.getContext());
+        }
+      catch(Exception ex)
+        {
+        android.util.Log.e("Effects3D", ex.getMessage() );
+        }
+      }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DSurfaceView.java b/src/main/java/org/distorted/examples/effects3d/Effects3DSurfaceView.java
new file mode 100644
index 0000000..ca6742b
--- /dev/null
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DSurfaceView.java
@@ -0,0 +1,126 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.effects3d;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Effects3DSurfaceView extends GLSurfaceView
+  {
+  private int mX, mY;
+  private Effects3DRenderer mRenderer;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+   
+  public Effects3DSurfaceView(Context c, AttributeSet attrs)
+    {
+    super(c, attrs);
+      
+    if(!isInEditMode())
+      {
+      setEGLContextClientVersion(2);
+        
+      if( Build.FINGERPRINT.startsWith("generic") )
+        {
+        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
+        }
+
+      mRenderer = new Effects3DRenderer(this);
+
+      setRenderer(mRenderer);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Effects3DRenderer getRenderer()
+    {
+    return mRenderer;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean onTouchEvent(MotionEvent event)
+    {
+    int action = event.getAction();
+    int x = (int)event.getX();
+    int y = (int)event.getY();
+
+    switch(action)
+      {
+      case MotionEvent.ACTION_DOWN: mX = x;
+                                    mY = y;
+                                    break;
+
+      case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
+                                      {
+                                      float px = mY-y;
+                                      float py = mX-x;
+                                      float pz = 0;
+                                      float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
+
+                                      if( plen>0 )
+                                        {
+                                        px /= plen;
+                                        py /= plen;
+                                        pz /= plen;
+
+                                        float cosA = (float)Math.cos(plen*3.14f/mRenderer.mScreenMin);
+                                        float sinA = (float)Math.sqrt(1-cosA*cosA);
+
+                                        mRenderer.mQuat1.set(px*sinA, py*sinA, pz*sinA, cosA);
+                                        }
+                                      }
+                                    break;
+
+      case MotionEvent.ACTION_UP  : mX = -1;
+                                    mY = -1;
+
+                                    float qx = mRenderer.mQuat1.getX();
+                                    float qy = mRenderer.mQuat1.getY();
+                                    float qz = mRenderer.mQuat1.getZ();
+                                    float qw = mRenderer.mQuat1.getW();
+
+                                    float rx = mRenderer.mQuat2.getX();
+                                    float ry = mRenderer.mQuat2.getY();
+                                    float rz = mRenderer.mQuat2.getZ();
+                                    float rw = mRenderer.mQuat2.getW();
+
+                                    float tx = rw*qx - rz*qy + ry*qz + rx*qw;
+                                    float ty = rw*qy + rz*qx + ry*qw - rx*qz;
+                                    float tz = rw*qz + rz*qw - ry*qx + rx*qy;
+                                    float tw = rw*qw - rz*qz - ry*qy - rx*qx;
+
+                                    mRenderer.mQuat1.set(0f, 0f, 0f, 1f);
+                                    mRenderer.mQuat2.set(tx, ty, tz, tw);
+
+                                    break;
+      }
+
+    return true;
+    }
+}
+
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java
deleted file mode 100644
index 69ff9ec..0000000
--- a/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java
+++ /dev/null
@@ -1,425 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.fragment3d;
-
-import android.app.Activity;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.NumberPicker;
-import android.widget.Spinner;
-import android.widget.TableRow;
-
-import org.distorted.examples.R;
-import org.distorted.library.Distorted;
-import org.distorted.library.DistortedBitmap;
-import org.distorted.library.DistortedCubes;
-import org.distorted.library.DistortedObject;
-import org.distorted.library.EffectNames;
-import org.distorted.library.EffectTypes;
-import org.distorted.library.type.Dynamic1D;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-
-import java.util.ArrayList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Fragment3DActivity extends Activity
-                                implements View.OnClickListener,
-                                           AdapterView.OnItemSelectedListener
-  {
-  private static final int COLOR_OFF = 0xffffe81f;
-  private static final int COLOR_ON  = 0xff0000ff;
-
-  private boolean firstScreen;
-
-  // fields needed for the first 'pick-a-shape' screen
-  //
-  private int mNumCols = 3;
-  private int mNumRows = 3;
-  private NumberPicker mColsPicker, mRowsPicker;
-  private boolean[] mShape;
-  private DistortedObject mObject;
-  private int mObjectType;
-  private int mBitmap;
-
-  private ArrayList<Fragment3DEffect> mEffects;
-  private int mEffectAdd;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onCreate(Bundle savedState)
-    {
-    super.onCreate(savedState);
-
-    mEffects = new ArrayList<>();
-
-    setContentView(R.layout.objectpickerlayout);
-
-    mColsPicker = (NumberPicker)findViewById(R.id.objectpicker_cols);
-    mRowsPicker = (NumberPicker)findViewById(R.id.objectpicker_rows);
-
-    mColsPicker.setMaxValue(10);
-    mColsPicker.setMinValue( 0);
-    mRowsPicker.setMaxValue(10);
-    mRowsPicker.setMinValue( 0);
-
-    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-      {
-      @Override
-      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-        {
-        mNumCols = mColsPicker.getValue();
-        }
-      });
-
-    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-      {
-      @Override
-      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-        {
-        mNumRows = mRowsPicker.getValue();
-        }
-      });
-
-    firstScreen = true;
-
-    mObjectType = 0;
-
-    Spinner typeSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerType);
-    typeSpinner.setOnItemSelectedListener(this);
-
-    String[] objectType = new String[] {"DistortedCubes", "DistortedBitmap"};
-
-    ArrayAdapter<String> adapterType = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectType);
-    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    typeSpinner.setAdapter(adapterType);
-
-    Spinner bitmapSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerBitmap);
-    bitmapSpinner.setOnItemSelectedListener(this);
-
-    String[] objectBitmap = new String[] {"Girl", "Dog", "Cat", "Grid"};
-
-    ArrayAdapter<String> adapterBitmap = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectBitmap);
-    adapterBitmap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    bitmapSpinner.setAdapter(adapterBitmap);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setGrid()
-    {
-    LinearLayout lay = (LinearLayout)findViewById(R.id.objectpicker_buttongrid);
-
-    int width = lay.getWidth();
-    int height= lay.getHeight();
-    int w = mNumCols>0 ? (width / mNumCols) -10 : 0;
-    int h = mNumRows>0 ? (height/ mNumRows) -10 : 0;
-    int size= w<h ? w:h;
-    int pad = size/20;
-
-    lay.removeAllViews();
-
-    mShape = new boolean[mNumRows*mNumCols];
-
-    TableRow.LayoutParams p = new android.widget.TableRow.LayoutParams();
-
-    p.rightMargin  = pad;
-    p.leftMargin   = pad;
-    p.topMargin    = pad;
-    p.bottomMargin = pad;
-    p.height       = size;
-    p.width        = size;
-
-    for (int rows=0; rows<mNumRows; rows++)
-      {
-      TableRow tr = new TableRow(this);
-      tr.setGravity(Gravity.CENTER);
-
-      for(int cols=0; cols<mNumCols; cols++)
-        {
-        Button b = new Button(this);
-        b.setOnClickListener(this);
-        b.setId(rows*mNumCols+cols);
-        b.setLayoutParams(p);
-        b.setBackgroundColor(COLOR_ON);
-        tr.addView(b, p);
-        mShape[rows*mNumCols+cols] = true;
-        }
-
-      lay.addView(tr);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public DistortedObject getObject()
-    {
-    return mObject;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getBitmap()
-    {
-    return mBitmap;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onClick(View view)
-    {
-    Button tmp = (Button)view;
-    int id = tmp.getId();
-    mShape[id] = !mShape[id];
-    tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void Continue(View v)
-    {
-    firstScreen = false;
-
-    if( mObjectType==1 )
-      {
-      mObject = new DistortedBitmap(100,100,mNumCols);
-      setFragmentView();
-      }
-    else
-      {
-      View view = getLayoutInflater().inflate(R.layout.objectpicker2layout, null);
-
-      setContentView(view);
-
-      view.post(new Runnable() {
-            @Override
-            public void run() {
-              setGrid();
-            }
-        });
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void Create(View v)
-    {
-    firstScreen = false;
-
-    String str = "";
-
-    for(int i=0; i<mNumRows*mNumCols; i++)
-      str += mShape[i] ? "1" : "0";
-
-    mObject = new DistortedCubes(mNumCols, str, 10);
-
-    setFragmentView();
-    }
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    switch(parent.getId())
-      {
-      case R.id.objectpicker_spinnerType  : mObjectType = pos;
-                                            break;
-      case R.id.objectpicker_spinnerBitmap: switch(pos)
-                                              {
-                                              case 0: mBitmap = R.raw.face; break;
-                                              case 1: mBitmap = R.raw.dog;  break;
-                                              case 2: mBitmap = R.raw.cat;  break;
-                                              case 3: mBitmap = R.raw.grid; break;
-                                              }
-                                            break;
-      case R.id.fragment3dspinner         : mEffectAdd = pos;
-                                            break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent)
-    {
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getWidth()
-    {
-    return mObject==null ? 0: mObject.getWidth();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getHeight()
-    {
-    return mObject==null ? 0: mObject.getHeight();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 'second screen' methods
-
-  private void setFragmentView()
-    {
-    final View view = getLayoutInflater().inflate(R.layout.fragment3dlayout, null);
-
-    setContentView(view);
-
-    String[] effects = new String[] { "ALPHA",
-                                      "SMOOTH_ALPHA",
-                                      "CHROMA",
-                                      "SMOOTH_CHROMA",
-                                      "BRIGHTNESS",
-                                      "SMOOTH_BRIGHTNESS",
-                                      "SATURATION",
-                                      "SMOOTH_SATURATION",
-                                      "CONTRAST",
-                                      "SMOOTH_CONTRAST" };
-
-    Spinner effectSpinner = (Spinner)findViewById(R.id.fragment3dspinner );
-    effectSpinner.setOnItemSelectedListener(this);
-
-    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, effects);
-    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    effectSpinner.setAdapter(adapterEffect);
-
-    mEffectAdd = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void newEffect(View v)
-    {
-    EffectNames name;
-
-    switch(mEffectAdd)
-      {
-      case 0 : name = EffectNames.ALPHA            ; break;
-      case 1 : name = EffectNames.SMOOTH_ALPHA     ; break;
-      case 2 : name = EffectNames.CHROMA           ; break;
-      case 3 : name = EffectNames.SMOOTH_CHROMA    ; break;
-      case 4 : name = EffectNames.BRIGHTNESS       ; break;
-      case 5 : name = EffectNames.SMOOTH_BRIGHTNESS; break;
-      case 6 : name = EffectNames.SATURATION       ; break;
-      case 7 : name = EffectNames.SMOOTH_SATURATION; break;
-      case 8 : name = EffectNames.CONTRAST         ; break;
-      case 9 : name = EffectNames.SMOOTH_CONTRAST  ; break;
-      default: return;
-      }
-
-    Fragment3DEffect eff = new Fragment3DEffect(name, this);
-    mEffects.add(eff);
-
-    LinearLayout layout = (LinearLayout)findViewById(R.id.fragment3dlayout);
-    View view = eff.createView();
-    layout.addView(view);
-    View region = eff.createRegion();
-    layout.addView(region);
-
-    Dynamic1D dyn1 = eff.getDyn1();
-    Dynamic3D dyn3 = eff.getDyn3();
-    Dynamic4D regi = eff.getRegion();
-
-    switch(mEffectAdd)
-      {
-      case 0: mObject.alpha     (dyn1,       regi, false); break;
-      case 1: mObject.alpha     (dyn1,       regi, true ); break;
-      case 2: mObject.chroma    (dyn1, dyn3, regi, false); break;
-      case 3: mObject.chroma    (dyn1, dyn3, regi, true ); break;
-      case 4: mObject.brightness(dyn1,       regi, false); break;
-      case 5: mObject.brightness(dyn1,       regi, true ); break;
-      case 6: mObject.saturation(dyn1,       regi, false); break;
-      case 7: mObject.saturation(dyn1,       regi, true ); break;
-      case 8: mObject.contrast  (dyn1,       regi, false); break;
-      case 9: mObject.contrast  (dyn1,       regi, true ); break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void removeAll(View v)
-    {
-    mEffects.clear();
-    LinearLayout layout = (LinearLayout)findViewById(R.id.fragment3dlayout);
-    layout.removeAllViews();
-    mObject.abortEffects(EffectTypes.FRAGMENT);
-
-    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
-    Fragment3DRenderer renderer= view.getRenderer();
-
-    renderer.mQuat1.set(0,0,0,1);
-    renderer.mQuat2.set(0,0,0,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Overrides
-
-  @Override
-  protected void onPause()
-    {
-    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.fragment3dSurfaceView);
-    if( mView!=null ) mView.onPause();
-    super.onPause();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onResume()
-    {
-    super.onResume();
-    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.fragment3dSurfaceView);
-    if( mView!=null ) mView.onResume();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  public void onWindowFocusChanged(boolean hasFocus)
-    {
-    super.onWindowFocusChanged(hasFocus);
-
-    if( firstScreen )
-      {
-      mColsPicker.setValue(mNumCols);
-      mRowsPicker.setValue(mNumRows);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onDestroy()
-    {
-    Distorted.onDestroy();
-    super.onDestroy();
-    }
-
-  }
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DEffect.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DEffect.java
deleted file mode 100644
index 80c7160..0000000
--- a/src/main/java/org/distorted/examples/fragment3d/Fragment3DEffect.java
+++ /dev/null
@@ -1,381 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.fragment3d;
-
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import org.distorted.examples.R;
-import org.distorted.library.EffectNames;
-import org.distorted.library.type.Dynamic1D;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-import org.distorted.library.type.Static1D;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-
-import java.lang.ref.WeakReference;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Fragment3DEffect implements SeekBar.OnSeekBarChangeListener
-  {
-  private WeakReference<Fragment3DActivity> mAct;
-
-  private EffectNames mName;
-  private int[] mInter;
-  private int[] mInterRegion;
-  private int[] mSeekID;
-  private int[] mSeekRegionID;
-  private int mDimension;
-  private TextView mText;
-  private TextView mTextRegion;
-
-  private Dynamic1D mDyn1;
-  private Dynamic3D mDyn3;
-  private Static1D  mSta1;
-  private Static3D  mSta3;
-  private Dynamic4D mRegionDyn;
-  private Static4D  mRegionSta;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillRegionStatics()
-    {
-    Fragment3DActivity act = mAct.get();
-
-    float factorX = act.getWidth() / 100.0f;
-    float factorY = act.getHeight()/ 100.0f;
-
-    float  x = mInterRegion[0]*factorX;
-    float  y = mInterRegion[1]*factorY;
-    float rx = mInterRegion[2]*factorX;
-    float ry = mInterRegion[3]*factorY;
-
-    mRegionSta.set(x,y,rx,ry);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultRegionInter()
-    {
-    mInterRegion[0] = 50;
-    mInterRegion[1] = 50;
-    mInterRegion[2] = 50;
-    mInterRegion[3] = 50;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setRegionText()
-    {
-    int f0 = (int)mRegionSta.getX();
-    int f1 = (int)mRegionSta.getY();
-    int f2 = (int)mRegionSta.getZ();
-    int f3 = (int)mRegionSta.getW();
-
-    mTextRegion.setText("region ("+f0+","+f1+","+f2+","+f3+")");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillStatics()
-    {
-    switch(mName)
-      {
-      case ALPHA            :
-      case SMOOTH_ALPHA     :
-      case BRIGHTNESS       :
-      case SMOOTH_BRIGHTNESS:
-      case SATURATION       :
-      case SMOOTH_SATURATION:
-      case CONTRAST         :
-      case SMOOTH_CONTRAST  : mSta1.set(mInter[0]/100.0f);
-                              break;
-
-      case CHROMA           :
-      case SMOOTH_CHROMA    : mSta1.set(mInter[0]/100.0f);
-                              mSta3.set(255.0f*mInter[1]/100,
-                                        255.0f*mInter[2]/100,
-                                        255.0f*mInter[3]/100);
-                              break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultInter()
-    {
-    switch(mName)
-      {
-      case ALPHA            :
-      case SMOOTH_ALPHA     :
-      case BRIGHTNESS       :
-      case SMOOTH_BRIGHTNESS:
-      case SATURATION       :
-      case SMOOTH_SATURATION:
-      case CONTRAST         :
-      case SMOOTH_CONTRAST  : mInter[0] = 50;
-                              break;
-
-      case CHROMA           :
-      case SMOOTH_CHROMA    : mInter[0] = 50;
-                              mInter[1] =  0;
-                              mInter[2] =  0;
-                              mInter[3] =  0;
-                              break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setText()
-    {
-    float f1 = ((int)(mSta1.getX()*100))/100.0f;
-
-    String text = mName.name() +" "+f1;
-
-    if( mName==EffectNames.CHROMA || mName==EffectNames.SMOOTH_CHROMA)
-      {
-      int f2 = (int)mSta3.getX();
-      int f3 = (int)mSta3.getY();
-      int f4 = (int)mSta3.getZ();
-      text += "("+f2+","+f3+","+f4+")";
-      }
-
-    mText.setText(text);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Fragment3DEffect(EffectNames name, Fragment3DActivity act)
-    {
-    mAct = new WeakReference<>(act);
-    mName = name;
-
-    if( mName==EffectNames.CHROMA || mName==EffectNames.SMOOTH_CHROMA )
-      {
-      mDimension = 4;
-      mDyn3 = new Dynamic3D();
-      mSta3 = new Static3D(0,0,0);
-      mDyn3.add(mSta3);
-      }
-    else
-      {
-      mDimension = 1;
-      mDyn3 = null;
-      mSta3 = null;
-      }
-
-    mInter = new int[mDimension];
-    mSeekID= new int[mDimension];
-
-    mDyn1 = new Dynamic1D();
-    mSta1 = new Static1D(0);
-    mDyn1.add(mSta1);
-
-    mInterRegion = new int[4];
-    mSeekRegionID= new int[4];
-    mRegionDyn   = new Dynamic4D();
-    mRegionSta   = new Static4D(0,0,0,0);
-    mRegionDyn.add(mRegionSta);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Dynamic1D getDyn1()
-    {
-    return mDyn1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Dynamic3D getDyn3()
-    {
-    return mDyn3;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Dynamic4D getRegion()
-    {
-    return mRegionDyn;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createView()
-    {
-    View effect;
-    SeekBar[] seek = new SeekBar[mDimension];
-
-    Fragment3DActivity act = mAct.get();
-
-    switch(mDimension)
-      {
-      case 1 : effect     = act.getLayoutInflater().inflate(R.layout.effect1d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect1dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect1dbar1);
-               mSeekID[0] = seek[0].getId();
-               break;
-      case 2 : effect     = act.getLayoutInflater().inflate(R.layout.effect2d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect2dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect2dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect2dbar2);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               break;
-      case 3 : effect     = act.getLayoutInflater().inflate(R.layout.effect3d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect3dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect3dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect3dbar2);
-               seek[2]    = (SeekBar)effect.findViewById(R.id.effect3dbar3);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               break;
-      case 4 : effect     = act.getLayoutInflater().inflate(R.layout.effect4d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect4dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect4dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect4dbar2);
-               seek[2]    = (SeekBar)effect.findViewById(R.id.effect4dbar3);
-               seek[3]    = (SeekBar)effect.findViewById(R.id.effect4dbar4);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               mSeekID[3] = seek[3].getId();
-               break;
-      default: android.util.Log.e("Vertex3DEffect", "dimension "+mDimension+" not supported!");
-               return null;
-      }
-
-    setDefaultInter();
-    fillStatics();
-    setText();
-
-    for(int i=0; i<mDimension; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInter[i] );
-      }
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createRegion()
-    {
-    Fragment3DActivity act = mAct.get();
-
-    View region = act.getLayoutInflater().inflate(R.layout.effectregion, null);
-
-    SeekBar[] seek = new SeekBar[4];
-
-    seek[0] = (SeekBar)region.findViewById(R.id.effectRegionBarX );
-    seek[1] = (SeekBar)region.findViewById(R.id.effectRegionBarY );
-    seek[2] = (SeekBar)region.findViewById(R.id.effectRegionBarRX);
-    seek[3] = (SeekBar)region.findViewById(R.id.effectRegionBarRY);
-
-    mSeekRegionID[0] = seek[0].getId();
-    mSeekRegionID[1] = seek[1].getId();
-    mSeekRegionID[2] = seek[2].getId();
-    mSeekRegionID[3] = seek[3].getId();
-
-    mTextRegion    = (TextView)region.findViewById(R.id.effectRegionText);
-
-    setDefaultRegionInter();
-    fillRegionStatics();
-    setRegionText();
-
-    for(int i=0; i<4; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInterRegion[i] );
-      }
-
-    return region;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
-    {
-    if ( mDimension>=1 && bar.getId()==mSeekID[0] )
-      {
-      mInter[0] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=2 && bar.getId()==mSeekID[1] )
-      {
-      mInter[1] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=3 && bar.getId()==mSeekID[2] )
-      {
-      mInter[2] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=4 && bar.getId()==mSeekID[3] )
-      {
-      mInter[3] = progress;
-      fillStatics();
-      setText();
-      }
-
-    if( bar.getId() == mSeekRegionID[0] )
-      {
-      mInterRegion[0] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[1] )
-      {
-      mInterRegion[1] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[2] )
-      {
-      mInterRegion[2] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[3] )
-      {
-      mInterRegion[3] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStartTrackingTouch(SeekBar bar) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStopTrackingTouch(SeekBar bar)  { }
-  }
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java
deleted file mode 100644
index 07cbfcb..0000000
--- a/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java
+++ /dev/null
@@ -1,163 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.fragment3d;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-
-import org.distorted.examples.R;
-import org.distorted.library.Distorted;
-import org.distorted.library.DistortedBitmap;
-import org.distorted.library.DistortedObject;
-import org.distorted.library.EffectTypes;
-import org.distorted.library.type.DynamicQuat;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Fragment3DRenderer implements GLSurfaceView.Renderer
-{
-    private GLSurfaceView mView;
-    private DistortedObject mObject;
-    private DistortedBitmap mBackground;
-    private int mObjWidth, mObjHeight;
-    private DynamicQuat mQuatInt1, mQuatInt2;
-    Static4D mQuat1, mQuat2;
-    int mScreenMin;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public Fragment3DRenderer(GLSurfaceView v)
-      {
-      mView = v;
-
-      mObject     = ((Fragment3DActivity)v.getContext()).getObject();
-      mBackground = new DistortedBitmap(100, 100, 1);
-
-      mObjWidth = mObject.getWidth();
-      mObjHeight= mObject.getHeight();
-
-      mQuat1 = new Static4D(0,0,0,1);  // unity
-      mQuat2 = new Static4D(0,0,0,1);  // quaternions
-
-      mQuatInt1 = new DynamicQuat(0,0.5f);
-      mQuatInt2 = new DynamicQuat(0,0.5f);
-
-      mQuatInt1.add(mQuat1);
-      mQuatInt2.add(mQuat2);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-    public void onDrawFrame(GL10 glUnused) 
-      {
-      GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-
-      long time = System.currentTimeMillis();
-
-      mBackground.draw(time);
-      mObject.draw(time);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    public void onSurfaceChanged(GL10 glUnused, int width, int height) 
-      {
-      mScreenMin = width<height ? width:height;
-
-      mObject.abortEffects(EffectTypes.MATRIX);
-      mBackground.abortEffects(EffectTypes.MATRIX);
-      float factorObj;
-
-      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
-        {
-        factorObj = (0.80f*height)/mObjHeight;
-        }
-      else
-        {
-        factorObj = (0.80f*width)/mObjWidth;
-        }
-
-      Static3D rotateObj = new Static3D(mObjWidth/2,mObjHeight/2, 0);
-
-      mObject.move( new Static3D( (width-factorObj*mObjWidth)/2 , (height-factorObj*mObjHeight)/2 , 0) );
-      mObject.scale(factorObj);
-      mObject.quaternion(mQuatInt1, rotateObj);
-      mObject.quaternion(mQuatInt2, rotateObj);
-
-      int backgroundSize = mBackground.getWidth();
-      float factorBackX = ((float)width)/backgroundSize;
-      float factorBackY = ((float)height)/backgroundSize;
-
-      mBackground.move(new Static3D( -width/2, -height/2,-factorObj*(mObjWidth+mObjHeight)/2) );
-      mBackground.scale(new Static3D(2*factorBackX, 2*factorBackY, 1.0f) );
-
-      Distorted.onSurfaceChanged(width, height); 
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
-      {
-      Fragment3DActivity act = (Fragment3DActivity)mView.getContext();
-
-      InputStream is1 = act.getResources().openRawResource(act.getBitmap());
-      InputStream is2 = act.getResources().openRawResource(R.raw.water);
-
-      Bitmap bitmap1,bitmap2;
-        
-      try 
-        {
-        bitmap1 = BitmapFactory.decodeStream(is1);
-        bitmap2 = BitmapFactory.decodeStream(is2);
-        }
-      finally 
-        {
-        try 
-          {
-          is1.close();
-          is2.close();
-          } 
-        catch(IOException e) { }
-        }  
-      
-      mObject.setBitmap(bitmap1);
-      mBackground.setBitmap(bitmap2)
-      ;
-      try
-        {
-        Distorted.onSurfaceCreated(mView.getContext());
-        }
-      catch(Exception ex)
-        {
-        android.util.Log.e("Fragment3D", ex.getMessage() );
-        }
-      }
-}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java
deleted file mode 100644
index e957a31..0000000
--- a/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java
+++ /dev/null
@@ -1,126 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.fragment3d;
-
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Fragment3DSurfaceView extends GLSurfaceView
-  {
-  private int mX, mY;
-  private Fragment3DRenderer mRenderer;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  public Fragment3DSurfaceView(Context c, AttributeSet attrs)
-    {
-    super(c, attrs);
-      
-    if(!isInEditMode())
-      {
-      setEGLContextClientVersion(2);
-        
-      if( Build.FINGERPRINT.startsWith("generic") )
-        {
-        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
-        }
-
-      mRenderer = new Fragment3DRenderer(this);
-
-      setRenderer(mRenderer);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Fragment3DRenderer getRenderer()
-    {
-    return mRenderer;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean onTouchEvent(MotionEvent event)
-    {
-    int action = event.getAction();
-    int x = (int)event.getX();
-    int y = (int)event.getY();
-
-    switch(action)
-      {
-      case MotionEvent.ACTION_DOWN: mX = x;
-                                    mY = y;
-                                    break;
-
-      case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
-                                      {
-                                      float px = mY-y;
-                                      float py = mX-x;
-                                      float pz = 0;
-                                      float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
-
-                                      if( plen>0 )
-                                        {
-                                        px /= plen;
-                                        py /= plen;
-                                        pz /= plen;
-
-                                        float cosA = (float)Math.cos(plen*3.14f/mRenderer.mScreenMin);
-                                        float sinA = (float)Math.sqrt(1-cosA*cosA);
-
-                                        mRenderer.mQuat1.set(px*sinA, py*sinA, pz*sinA, cosA);
-                                        }
-                                      }
-                                    break;
-
-      case MotionEvent.ACTION_UP  : mX = -1;
-                                    mY = -1;
-
-                                    float qx = mRenderer.mQuat1.getX();
-                                    float qy = mRenderer.mQuat1.getY();
-                                    float qz = mRenderer.mQuat1.getZ();
-                                    float qw = mRenderer.mQuat1.getW();
-
-                                    float rx = mRenderer.mQuat2.getX();
-                                    float ry = mRenderer.mQuat2.getY();
-                                    float rz = mRenderer.mQuat2.getZ();
-                                    float rw = mRenderer.mQuat2.getW();
-
-                                    float tx = rw*qx - rz*qy + ry*qz + rx*qw;
-                                    float ty = rw*qy + rz*qx + ry*qw - rx*qz;
-                                    float tz = rw*qz + rz*qw - ry*qx + rx*qy;
-                                    float tw = rw*qw - rz*qz - ry*qy - rx*qx;
-
-                                    mRenderer.mQuat1.set(0f, 0f, 0f, 1f);
-                                    mRenderer.mQuat2.set(tx, ty, tz, tw);
-
-                                    break;
-      }
-
-    return true;
-    }
-}
-
diff --git a/src/main/java/org/distorted/examples/vertex3d/Vertex3DActivity.java b/src/main/java/org/distorted/examples/vertex3d/Vertex3DActivity.java
deleted file mode 100644
index 8df4bca..0000000
--- a/src/main/java/org/distorted/examples/vertex3d/Vertex3DActivity.java
+++ /dev/null
@@ -1,563 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.vertex3d;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.NumberPicker;
-import android.widget.Spinner;
-import android.widget.TableRow;
-
-import org.distorted.examples.R;
-import org.distorted.library.Distorted;
-import org.distorted.library.DistortedBitmap;
-import org.distorted.library.DistortedCubes;
-import org.distorted.library.DistortedObject;
-import org.distorted.library.EffectNames;
-import org.distorted.library.EffectTypes;
-import org.distorted.library.type.Dynamic1D;
-import org.distorted.library.type.Dynamic2D;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-import org.distorted.library.type.Static3D;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Vertex3DActivity extends Activity
-                              implements View.OnClickListener,
-                                         AdapterView.OnItemSelectedListener
-  {
-  private static final int COLOR_OFF = 0xffffe81f;
-  private static final int COLOR_ON  = 0xff0000ff;
-
-  private boolean firstScreen;
-
-  // fields needed for the first 'pick-a-shape' screen
-  //
-  private int mNumCols = 10;
-  private int mNumRows = 10;
-  private NumberPicker mColsPicker, mRowsPicker;
-  private boolean[] mShape;
-  private DistortedObject mObject;
-  private int mObjectType;
-  private int mBitmap;
-
-  private ArrayList<Vertex3DEffect> mEffects;
-  private int mEffectAdd;
-  private float mCenterX, mCenterY;
-  private float mRegionX, mRegionY, mRegionR;
-
-  private EffectNames[] mEffectNames;
-
-  private static boolean mSupportsRegion = true;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  protected void onCreate(Bundle savedState)
-    {
-    super.onCreate(savedState);
-
-    mEffects = new ArrayList<>();
-
-    createEffectNames();
-
-    setContentView(R.layout.objectpickerlayout);
-
-    mColsPicker = (NumberPicker)findViewById(R.id.objectpicker_cols);
-    mRowsPicker = (NumberPicker)findViewById(R.id.objectpicker_rows);
-
-    mColsPicker.setMaxValue(15);
-    mColsPicker.setMinValue( 0);
-    mRowsPicker.setMaxValue(15);
-    mRowsPicker.setMinValue( 0);
-
-    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-      {
-      @Override
-      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-        {
-        mNumCols = mColsPicker.getValue();
-        }
-      });
-
-    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-      {
-      @Override
-      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-        {
-        mNumRows = mRowsPicker.getValue();
-        }
-      });
-
-    firstScreen = true;
-
-    mObjectType = 0;
-
-    Spinner typeSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerType);
-    typeSpinner.setOnItemSelectedListener(this);
-
-    String[] objectType = new String[] {"DistortedCubes", "DistortedBitmap"};
-
-    ArrayAdapter<String> adapterType = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectType);
-    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    typeSpinner.setAdapter(adapterType);
-
-    Spinner bitmapSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerBitmap);
-    bitmapSpinner.setOnItemSelectedListener(this);
-
-    String[] objectBitmap = new String[] { "Grid", "Girl", "Dog", "Cat", "Squares"};
-
-    ArrayAdapter<String> adapterBitmap = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectBitmap);
-    adapterBitmap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    bitmapSpinner.setAdapter(adapterBitmap);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createEffectNames()
-    {
-    EffectNames[] names = EffectNames.values();
-
-    int numVertex=0;
-
-    for(int i=0; i<names.length; i++)
-      if( names[i].getType() == EffectTypes.VERTEX ) numVertex++;
-
-    mEffectNames = new EffectNames[numVertex];
-
-    numVertex=0;
-
-    for(int i=0; i<names.length; i++)
-      if( names[i].getType() == EffectTypes.VERTEX )
-        {
-        mEffectNames[numVertex++] = names[i];
-        }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setGrid()
-    {
-    LinearLayout lay = (LinearLayout)findViewById(R.id.objectpicker_buttongrid);
-
-    int width = lay.getWidth();
-    int height= lay.getHeight();
-    int w = mNumCols>0 ? (width / mNumCols) -10 : 0;
-    int h = mNumRows>0 ? (height/ mNumRows) -10 : 0;
-    int size= w<h ? w:h;
-    int pad = size/20;
-
-    lay.removeAllViews();
-
-    mShape = new boolean[mNumRows*mNumCols];
-
-    TableRow.LayoutParams p = new TableRow.LayoutParams();
-
-    p.rightMargin  = pad;
-    p.leftMargin   = pad;
-    p.topMargin    = pad;
-    p.bottomMargin = pad;
-    p.height       = size;
-    p.width        = size;
-
-    for (int rows=0; rows<mNumRows; rows++)
-      {
-      TableRow tr = new TableRow(this);
-      tr.setGravity(Gravity.CENTER);
-
-      for(int cols=0; cols<mNumCols; cols++)
-        {
-        Button b = new Button(this);
-        b.setOnClickListener(this);
-        b.setId(rows*mNumCols+cols);
-        b.setLayoutParams(p);
-        b.setBackgroundColor(COLOR_ON);
-        tr.addView(b, p);
-        mShape[rows*mNumCols+cols] = true;
-        }
-
-      lay.addView(tr);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public DistortedObject getObject()
-    {
-    return mObject;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void setSupportsRegion(boolean supports)
-    {
-    mSupportsRegion = supports;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean supportsRegion()
-    {
-    return mSupportsRegion;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Bitmap getBitmap()
-    {
-    Bitmap bmp;
-
-    if( mBitmap!=-1)
-      {
-      InputStream is = getResources().openRawResource(mBitmap);
-
-      try
-        {
-        bmp = BitmapFactory.decodeStream(is);
-        }
-      finally
-        {
-        try
-          {
-          is.close();
-          }
-        catch(IOException e) { }
-        }
-      }
-    else
-      {
-      final int W = 640;
-      final int H = 640;
-
-      Paint paint = new Paint();
-      bmp = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(bmp);
-
-      paint.setAntiAlias(true);
-      paint.setTextAlign(Paint.Align.CENTER);
-      paint.setColor(0xff008800);
-      paint.setStyle(Paint.Style.FILL);
-      canvas.drawRect(0, 0, W, H, paint);
-      paint.setColor(0xffffffff);
-
-      for(int i=0; i<=mNumCols ; i++ )
-        {
-        canvas.drawRect(W*i/mNumCols - 1,                0,  W*i/mNumCols + 1,  H               , paint);
-        canvas.drawRect(               0, H *i/mNumCols -1,  W               ,  H*i/mNumCols + 1, paint);
-        }
-      }
-
-    return bmp;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onClick(View view)
-    {
-    Button tmp = (Button)view;
-    int id = tmp.getId();
-    mShape[id] = !mShape[id];
-    tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setRegion(float x, float y, float r)
-    {
-    mRegionX = x;
-    mRegionY =-y;
-    mRegionR = r;
-
-    Vertex3DSurfaceView view = (Vertex3DSurfaceView)findViewById(R.id.vertex3dSurfaceView);
-
-    view.getRenderer().setRegion(mRegionX, mRegionY, mRegionR);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setCenter(float x, float y)
-    {
-    mCenterX = x;
-    mCenterY = y;
-
-    Vertex3DSurfaceView view = (Vertex3DSurfaceView)findViewById(R.id.vertex3dSurfaceView);
-
-    view.getRenderer().setCenter( mCenterX, mCenterY );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getCenterX()
-    {
-    return mCenterX;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getCenterY()
-    {
-    return mCenterY;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionX()
-    {
-    return mRegionX;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionY()
-    {
-    return mRegionY;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionR()
-    {
-    return mRegionR;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void Continue(View v)
-    {
-    firstScreen = false;
-
-    if( mObjectType==1 )
-      {
-      mObject = new DistortedBitmap(100,100,mNumCols);
-      setVertexView();
-      }
-    else
-      {
-      View view = getLayoutInflater().inflate(R.layout.objectpicker2layout, null);
-
-      setContentView(view);
-
-      view.post(new Runnable() {
-            @Override
-            public void run() {
-              setGrid();
-            }
-        });
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void Create(View v)
-    {
-    firstScreen = false;
-
-    String str = "";
-
-    for(int i=0; i<mNumRows*mNumCols; i++)
-      str += mShape[i] ? "1" : "0";
-
-    mObject = new DistortedCubes(mNumCols, str, 10);
-
-    setVertexView();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    switch(parent.getId())
-      {
-      case R.id.objectpicker_spinnerType  : mObjectType = pos;
-                                            break;
-      case R.id.objectpicker_spinnerBitmap: switch(pos)
-                                              {
-                                              case 0: mBitmap = -1        ; break;
-                                              case 1: mBitmap = R.raw.face; break;
-                                              case 2: mBitmap = R.raw.dog;  break;
-                                              case 3: mBitmap = R.raw.cat;  break;
-                                              case 4: mBitmap = R.raw.grid; break;
-                                              }
-                                            break;
-      case R.id.vertex3dspinner           : mEffectAdd = pos;
-                                            break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent)
-    {
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getWidth()
-    {
-    return mObject==null ? 0: mObject.getWidth();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getHeight()
-    {
-    return mObject==null ? 0: mObject.getHeight();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetData()
-    {
-    mCenterX = 0.5f*getWidth();
-    mCenterY = 0.5f*getHeight();
-    mRegionX = 0;
-    mRegionY = 0;
-    mRegionR = getWidth()/2;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// 'second screen' methods
-
-  private void setVertexView()
-    {
-    resetData();
-
-    final View view = getLayoutInflater().inflate(R.layout.vertex3dlayout, null);
-
-    setContentView(view);
-
-    String[] effects = new String[mEffectNames.length];
-
-    for(int i=0; i<mEffectNames.length; i++) effects[i] = mEffectNames[i].name();
-
-    Spinner effectSpinner = (Spinner)findViewById(R.id.vertex3dspinner );
-    effectSpinner.setOnItemSelectedListener(this);
-
-    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, effects);
-    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    effectSpinner.setAdapter(adapterEffect);
-
-    mEffectAdd = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void newEffect(View v)
-    {
-    Vertex3DEffect eff = new Vertex3DEffect(mEffectNames[mEffectAdd], this);
-    mEffects.add(eff);
-
-    LinearLayout layout = (LinearLayout)findViewById(R.id.vertex3dlayout);
-    View view = eff.createView();
-    layout.addView(view);
-
-    if( eff.supportsRegion() )
-      {
-      View region = eff.createRegion();
-      layout.addView(region);
-      }
-
-    View center = eff.createCenter();
-    layout.addView(center);
-
-    eff.apply(mObject);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void removeAll(View v)
-    {
-    mEffects.clear();
-    LinearLayout layout = (LinearLayout)findViewById(R.id.vertex3dlayout);
-    layout.removeAllViews();
-    mObject.abortEffects(EffectTypes.VERTEX);
-
-    resetData();
-
-    Vertex3DSurfaceView view = (Vertex3DSurfaceView)findViewById(R.id.vertex3dSurfaceView);
-    Vertex3DRenderer renderer= view.getRenderer();
-
-    renderer.setCenter( mCenterX, mCenterY );
-    renderer.setRegion( mRegionX, mRegionY, mRegionR );
-    renderer.mQuat1.set(0,0,0,1);
-    renderer.mQuat2.set(0,0,0,1);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Overrides
-
-  @Override
-  protected void onPause()
-    {
-    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.vertex3dSurfaceView);
-    if( mView!=null ) mView.onPause();
-    super.onPause();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onResume()
-    {
-    super.onResume();
-    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.vertex3dSurfaceView);
-    if( mView!=null ) mView.onResume();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  public void onWindowFocusChanged(boolean hasFocus)
-    {
-    super.onWindowFocusChanged(hasFocus);
-
-    if( firstScreen )
-      {
-      mColsPicker.setValue(mNumCols);
-      mRowsPicker.setValue(mNumRows);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onDestroy()
-    {
-    Distorted.onDestroy();
-    super.onDestroy();
-    }
-
-  }
diff --git a/src/main/java/org/distorted/examples/vertex3d/Vertex3DEffect.java b/src/main/java/org/distorted/examples/vertex3d/Vertex3DEffect.java
deleted file mode 100644
index 7d92d01..0000000
--- a/src/main/java/org/distorted/examples/vertex3d/Vertex3DEffect.java
+++ /dev/null
@@ -1,488 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.vertex3d;
-
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import org.distorted.examples.R;
-import org.distorted.library.DistortedObject;
-import org.distorted.library.EffectNames;
-import org.distorted.library.type.Dynamic1D;
-import org.distorted.library.type.Dynamic2D;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-import org.distorted.library.type.Static1D;
-import org.distorted.library.type.Static2D;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-
-import java.lang.ref.WeakReference;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Vertex3DEffect implements SeekBar.OnSeekBarChangeListener
-  {
-  private WeakReference<Vertex3DActivity> mAct;
-
-  private EffectNames mName;
-  private int[] mInter;
-  private int[] mInterRegion;
-  private int[] mInterCenter;
-  private int[] mSeekID;
-  private int[] mSeekRegionID;
-  private int[] mSeekCenterID;
-  private int mDimension;
-  private TextView mText,mTextRegion,mTextCenter;
-
-  private Dynamic1D mDyn1;
-  private Dynamic3D mDyn3;
-  private Static1D  mSta1;
-  private Static3D  mSta3;
-  private Dynamic4D mRegionDyn;
-  private Static4D  mRegionSta;
-  private Dynamic2D mCenterDyn;
-  private Static2D  mCenterSta;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  private int getDimension()
-    {
-    switch(mName)
-      {
-      case SWIRL  :
-      case SINK   : return 1;
-      case DISTORT:
-      case DEFORM : return 3;
-      }
-
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  public void apply(DistortedObject object)
-    {
-    switch(mName)
-      {
-      case DISTORT: object.distort(mDyn3, mCenterDyn, mRegionDyn); break;
-      case DEFORM : object.deform (mDyn3, mCenterDyn            ); break;
-      case SINK   : object.sink   (mDyn1, mCenterDyn, mRegionDyn); break;
-      case SWIRL  : object.swirl  (mDyn1, mCenterDyn, mRegionDyn); break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  public boolean supportsRegion()
-    {
-    return mName == EffectNames.DEFORM ? false : true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  private void fillStatics()
-    {
-    switch(mName)
-      {
-      case DISTORT:
-      case DEFORM : float l = mAct.get().getWidth()/50.0f;
-                    float x = (mInter[0]-50)*l;
-                    float y = (mInter[1]-50)*l;
-                    float z = (mInter[2]-50)*l;
-                    mSta3.set(x,y,z);
-                    break;
-      case SINK   : mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
-                    break;
-      case SWIRL  : mSta1.set( 3.6f*(mInter[0]-50) );
-                    break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  private void setDefaultInter()
-    {
-    switch(mName)
-      {
-      case DISTORT:
-      case DEFORM : mInter[0] = 50;
-                    mInter[1] = 50;
-                    mInter[2] = 50;
-                    break;
-      case SINK   :
-      case SWIRL  : mInter[0] = 50;
-                    break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  private void setText()
-    {
-    String text = mName.name();
-
-    if( mName==EffectNames.DISTORT || mName==EffectNames.DEFORM)
-      {
-      int f1 = (int)mSta3.getX();
-      int f2 = (int)mSta3.getY();
-      int f3 = (int)mSta3.getZ();
-      text += " ("+f1+","+f2+","+f3+")";
-      }
-    else
-      {
-      float f1 = ((int)(mSta1.getX()*100))/100.0f;
-      text += " ("+f1+")";
-      }
-
-    mText.setText(text);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillCenterStatics()
-    {
-    Vertex3DActivity act = mAct.get();
-
-    float x = (mInterCenter[0]*0.012f - 0.1f)*act.getWidth();
-    float y = (mInterCenter[1]*0.012f - 0.1f)*act.getHeight();
-    mCenterSta.set(x,y);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultCenterInter()
-    {
-    mInterCenter[0] = 50;
-    mInterCenter[1] = 50;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setCenterText()
-    {
-    int f0 = (int)mCenterSta.getX();
-    int f1 = (int)mCenterSta.getY();
-
-    mTextCenter.setText("center ("+f0+","+f1+")");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillRegionStatics()
-    {
-    Vertex3DActivity act = mAct.get();
-
-    float factorX = act.getWidth() / 100.0f;
-    float factorY = act.getHeight()/ 100.0f;
-
-    float  x = (mInterRegion[0]-50)*factorX;
-    float  y = (mInterRegion[1]-50)*factorY;
-    float rx =  mInterRegion[2]    *factorX;
-    float ry =  mInterRegion[3]    *factorY;
-
-    mRegionSta.set(x,y,rx,ry);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultRegionInter()
-    {
-    mInterRegion[0] = 50;
-    mInterRegion[1] = 50;
-    mInterRegion[2] = 50;
-    mInterRegion[3] = 50;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setRegionText()
-    {
-    int f0 = (int)mRegionSta.getX();
-    int f1 = (int)mRegionSta.getY();
-    int f2 = (int)mRegionSta.getZ();
-    int f3 = (int)mRegionSta.getW();
-
-    mTextRegion.setText("region ("+f0+","+f1+","+f2+","+f3+")");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Vertex3DEffect(EffectNames name, Vertex3DActivity act)
-    {
-    mAct = new WeakReference<>(act);
-    mName = name;
-
-    mDimension = getDimension();
-
-    if( mDimension==1 )
-      {
-      mDyn1 = new Dynamic1D();
-      mSta1 = new Static1D(0);
-      mDyn1.add(mSta1);
-      mDyn3 = null;
-      }
-    else if( mDimension==3 )
-      {
-      mDyn3 = new Dynamic3D();
-      mSta3 = new Static3D(0,0,0);
-      mDyn3.add(mSta3);
-      mDyn1 = null;
-      }
-    else
-      {
-      throw new RuntimeException("unsupported effect");
-      }
-
-    mInter = new int[mDimension];
-    mSeekID= new int[mDimension];
-
-    mInterRegion = new int[4];
-    mSeekRegionID= new int[4];
-    mRegionDyn   = new Dynamic4D();
-    mRegionSta   = new Static4D(0,0,0,0);
-    mRegionDyn.add(mRegionSta);
-
-    mInterCenter = new int[2];
-    mSeekCenterID= new int[2];
-    mCenterDyn   = new Dynamic2D();
-    mCenterSta   = new Static2D(0,0);
-    mCenterDyn.add(mCenterSta);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createView()
-    {
-    View effect;
-    SeekBar[] seek = new SeekBar[mDimension];
-
-    Vertex3DActivity act = mAct.get();
-
-    switch(mDimension)
-      {
-      case 1 : effect     = act.getLayoutInflater().inflate(R.layout.effect1d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect1dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect1dbar1);
-               mSeekID[0] = seek[0].getId();
-               break;
-      case 2 : effect     = act.getLayoutInflater().inflate(R.layout.effect2d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect2dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect2dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect2dbar2);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               break;
-      case 3 : effect     = act.getLayoutInflater().inflate(R.layout.effect3d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect3dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect3dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect3dbar2);
-               seek[2]    = (SeekBar)effect.findViewById(R.id.effect3dbar3);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               break;
-      case 4 : effect     = act.getLayoutInflater().inflate(R.layout.effect4d, null);
-               mText      = (TextView)effect.findViewById(R.id.effect4dText);
-               seek[0]    = (SeekBar)effect.findViewById(R.id.effect4dbar1);
-               seek[1]    = (SeekBar)effect.findViewById(R.id.effect4dbar2);
-               seek[2]    = (SeekBar)effect.findViewById(R.id.effect4dbar3);
-               seek[3]    = (SeekBar)effect.findViewById(R.id.effect4dbar4);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               mSeekID[3] = seek[3].getId();
-               break;
-      default: android.util.Log.e("Vertex3DEffect", "dimension "+mDimension+" not supported!");
-               return null;
-      }
-
-    setDefaultInter();
-
-    for(int i=0; i<mDimension; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInter[i] );
-      }
-
-    Vertex3DActivity.setSupportsRegion(supportsRegion());
-
-    return effect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createRegion()
-    {
-    Vertex3DActivity act = mAct.get();
-
-    View region = act.getLayoutInflater().inflate(R.layout.effectregion, null);
-
-    SeekBar[] seek = new SeekBar[4];
-
-    seek[0] = (SeekBar)region.findViewById(R.id.effectRegionBarX );
-    seek[1] = (SeekBar)region.findViewById(R.id.effectRegionBarY );
-    seek[2] = (SeekBar)region.findViewById(R.id.effectRegionBarRX);
-    seek[3] = (SeekBar)region.findViewById(R.id.effectRegionBarRY);
-
-    mSeekRegionID[0] = seek[0].getId();
-    mSeekRegionID[1] = seek[1].getId();
-    mSeekRegionID[2] = seek[2].getId();
-    mSeekRegionID[3] = seek[3].getId();
-
-    mTextRegion = (TextView)region.findViewById(R.id.effectRegionText);
-
-    setDefaultRegionInter();
-
-    for(int i=0; i<4; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInterRegion[i] );
-      }
-
-    act.setRegion(mRegionSta.getX(),mRegionSta.getY(),mRegionSta.getZ());
-
-    return region;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View createCenter()
-    {
-    Vertex3DActivity act = mAct.get();
-
-    View center = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
-
-    SeekBar[] seek = new SeekBar[2];
-
-    seek[0] = (SeekBar)center.findViewById(R.id.effectCenterBarX );
-    seek[1] = (SeekBar)center.findViewById(R.id.effectCenterBarY );
-
-    mSeekCenterID[0] = seek[0].getId();
-    mSeekCenterID[1] = seek[1].getId();
-
-    mTextCenter = (TextView)center.findViewById(R.id.effectCenterText);
-
-    setDefaultCenterInter();
-
-    for(int i=0; i<2; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInterCenter[i] );
-      }
-
-    act.setCenter(mCenterSta.getX(),mCenterSta.getY());
-
-    return center;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
-    {
-    if ( mDimension>=1 && bar.getId()==mSeekID[0] )
-      {
-      mInter[0] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=2 && bar.getId()==mSeekID[1] )
-      {
-      mInter[1] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=3 && bar.getId()==mSeekID[2] )
-      {
-      mInter[2] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=4 && bar.getId()==mSeekID[3] )
-      {
-      mInter[3] = progress;
-      fillStatics();
-      setText();
-      }
-
-    if( bar.getId() == mSeekRegionID[0] )
-      {
-      mInterRegion[0] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[1] )
-      {
-      mInterRegion[1] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[2] )
-      {
-      mInterRegion[2] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( bar.getId() == mSeekRegionID[3] )
-      {
-      mInterRegion[3] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-
-    if( bar.getId() == mSeekCenterID[0] )
-      {
-      mInterCenter[0] = progress;
-      fillCenterStatics();
-      setCenterText();
-      }
-    if( bar.getId() == mSeekCenterID[1] )
-      {
-      mInterCenter[1] = progress;
-      fillCenterStatics();
-      setCenterText();
-      }
-
-    if( fromUser )
-      {
-      Vertex3DActivity.setSupportsRegion(supportsRegion());
-
-      Vertex3DActivity act = mAct.get();
-
-      act.setCenter(mCenterSta.getX(),mCenterSta.getY());
-      act.setRegion(mRegionSta.getX(),mRegionSta.getY(),mRegionSta.getZ());
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStartTrackingTouch(SeekBar bar) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStopTrackingTouch(SeekBar bar)  { }
-  }
diff --git a/src/main/java/org/distorted/examples/vertex3d/Vertex3DRenderer.java b/src/main/java/org/distorted/examples/vertex3d/Vertex3DRenderer.java
deleted file mode 100644
index d10a894..0000000
--- a/src/main/java/org/distorted/examples/vertex3d/Vertex3DRenderer.java
+++ /dev/null
@@ -1,247 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.vertex3d;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-
-import org.distorted.examples.R;
-import org.distorted.library.Distorted;
-import org.distorted.library.DistortedBitmap;
-import org.distorted.library.DistortedObject;
-import org.distorted.library.EffectTypes;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.DynamicQuat;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Vertex3DRenderer implements GLSurfaceView.Renderer
-{
-    private GLSurfaceView mView;
-    private DistortedObject mObject;
-    private DistortedBitmap mBackground;
-    private DistortedBitmap mCenter;
-    private DistortedBitmap mRegion;
-    private int mObjWidth, mObjHeight, mObjDepth;
-    private DynamicQuat mQuatInt1, mQuatInt2;
-
-    private Dynamic3D mCenterInter, mRegionInter;
-    private Static3D mCenterPoint, mRegionPoint;
-    private Dynamic3D mRegionScaleInter;
-    private Static3D mRegionScalePoint;
-
-    private float mFactorObj, mFactorReg;
-
-    Static4D mQuat1, mQuat2;
-    int mScreenMin;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public Vertex3DRenderer(GLSurfaceView v)
-      {
-      mView = v;
-
-      mObject     = ((Vertex3DActivity)v.getContext()).getObject();
-      mBackground = new DistortedBitmap(100, 100, 1);
-      mCenter     = new DistortedBitmap(100, 100, 1);
-      mRegion     = new DistortedBitmap(100, 100, 1);
-
-      mObjWidth = mObject.getWidth();
-      mObjHeight= mObject.getHeight();
-      mObjDepth = mObject.getDepth();
-
-      mQuat1 = new Static4D(0,0,0,1);  // unity
-      mQuat2 = new Static4D(0,0,0,1);  // quaternions
-
-      mQuatInt1 = new DynamicQuat(0,0.5f);
-      mQuatInt2 = new DynamicQuat(0,0.5f);
-
-      mQuatInt1.add(mQuat1);
-      mQuatInt2.add(mQuat2);
-
-      mCenterInter= new Dynamic3D();
-      mCenterPoint= new Static3D(0,0,0);
-      mCenterInter.add(mCenterPoint);
-
-      mRegionInter= new Dynamic3D();
-      mRegionPoint= new Static3D(0,0,0);
-      mRegionInter.add(mRegionPoint);
-
-      mRegionScaleInter = new Dynamic3D();
-      mRegionScalePoint = new Static3D(0,0,0);
-      mRegionScaleInter.add(mRegionScalePoint);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void setCenter(float x, float y)
-      {
-      mCenterPoint.set(mFactorObj*x,mFactorObj*y,0);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void setRegion(float x, float y, float r)
-      {
-      mFactorReg = 2*mFactorObj*r/mRegion.getWidth();
-      mRegionPoint.set(mFactorObj*x,mFactorObj*y,0);
-      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onDrawFrame(GL10 glUnused)
-      {
-      GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-
-      long time = System.currentTimeMillis();
-
-      mBackground.draw(time);
-      mObject.draw(time);
-      mCenter.draw(time);
-
-      if( Vertex3DActivity.supportsRegion() ) mRegion.draw(time);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onSurfaceChanged(GL10 glUnused, int width, int height)
-      {
-      mScreenMin = width<height ? width:height;
-
-      mObject.abortEffects(EffectTypes.MATRIX);
-      mBackground.abortEffects(EffectTypes.MATRIX);
-      mCenter.abortEffects(EffectTypes.MATRIX);
-      mRegion.abortEffects(EffectTypes.MATRIX);
-
-      float factorCen;
-      int centerSize = mCenter.getWidth();
-      int regionSize = mRegion.getWidth();
-
-      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
-        {
-        mFactorObj = (0.80f*height)/mObjHeight;
-        factorCen  = (0.08f*height)/centerSize;
-        }
-      else
-        {
-        mFactorObj = (0.80f*width)/mObjWidth;
-        factorCen  = (0.08f*width)/centerSize;
-        }
-
-      Vertex3DActivity act = (Vertex3DActivity)mView.getContext();
-      mCenterPoint.set(mFactorObj*act.getCenterX(),mFactorObj*act.getCenterY(),0);
-      mRegionPoint.set(mFactorObj*act.getRegionX(),mFactorObj*act.getRegionY(),0);
-
-      mFactorReg = 2*mFactorObj*act.getRegionR()/regionSize;
-      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
-
-      Static3D rotateObj = new Static3D(mObjWidth/2,mObjHeight/2, 0);
-
-      mObject.move( new Static3D( (width-mFactorObj*mObjWidth)/2 , (height-mFactorObj*mObjHeight)/2 , 0) );
-      mObject.scale(mFactorObj);
-      mObject.quaternion(mQuatInt1, rotateObj);
-      mObject.quaternion(mQuatInt2, rotateObj);
-
-      Static3D rotateCen = new Static3D(width/2,height/2, 0);
-
-      mCenter.quaternion(mQuatInt1, rotateCen);
-      mCenter.quaternion(mQuatInt2, rotateCen);
-
-      mCenter.move( new Static3D( (width -factorCen*centerSize-mFactorObj*mObjWidth )/2 ,
-                                  (height-factorCen*centerSize-mFactorObj*mObjHeight)/2 , mFactorObj*mObjDepth/2+10) );
-      mCenter.move(mCenterInter);
-      mCenter.scale(factorCen);
-
-      mRegion.quaternion(mQuatInt1, rotateCen);
-      mRegion.quaternion(mQuatInt2, rotateCen);
-
-      mRegion.move( new Static3D( (width -mFactorObj*mObjWidth )/2 ,
-                                  (height-mFactorObj*mObjHeight)/2 , mFactorObj*mObjDepth/2+12) );
-      mRegion.move(mCenterInter);
-      mRegion.move(mRegionInter);
-      mRegion.scale(mRegionScaleInter);
-      mRegion.move( new Static3D( -regionSize/2 , -regionSize/2 , 0) );
-
-      int backgroundSize = mBackground.getWidth();
-      float factorBackX = ((float)width)/backgroundSize;
-      float factorBackY = ((float)height)/backgroundSize;
-
-      mBackground.move(new Static3D( -width/2, -height/2,-mFactorObj*(mObjWidth+mObjHeight)/2) );
-      mBackground.scale(new Static3D(2*factorBackX, 2*factorBackY, 1.0f) );
-
-      Distorted.onSurfaceChanged(width, height);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
-      {
-      Vertex3DActivity act = (Vertex3DActivity)mView.getContext();
-
-      InputStream is1 = act.getResources().openRawResource(R.raw.water);
-      InputStream is2 = act.getResources().openRawResource(R.raw.center);
-      InputStream is3 = act.getResources().openRawResource(R.raw.region);
-
-      Bitmap bitmap1,bitmap2,bitmap3;
-        
-      try 
-        {
-        bitmap1 = BitmapFactory.decodeStream(is1);
-        bitmap2 = BitmapFactory.decodeStream(is2);
-        bitmap3 = BitmapFactory.decodeStream(is3);
-        }
-      finally 
-        {
-        try 
-          {
-          is1.close();
-          is2.close();
-          is3.close();
-          }
-        catch(IOException e) { }
-        }  
-      
-      mObject.setBitmap( act.getBitmap() );
-      mBackground.setBitmap(bitmap1);
-      mCenter.setBitmap(bitmap2);
-      mRegion.setBitmap(bitmap3);
-
-      try
-        {
-        Distorted.onSurfaceCreated(mView.getContext());
-        }
-      catch(Exception ex)
-        {
-        android.util.Log.e("Vertex3D", ex.getMessage() );
-        }
-      }
-}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/vertex3d/Vertex3DSurfaceView.java b/src/main/java/org/distorted/examples/vertex3d/Vertex3DSurfaceView.java
deleted file mode 100644
index a32bb00..0000000
--- a/src/main/java/org/distorted/examples/vertex3d/Vertex3DSurfaceView.java
+++ /dev/null
@@ -1,126 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.vertex3d;
-
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Vertex3DSurfaceView extends GLSurfaceView
-  {
-  private int mX, mY;
-  private Vertex3DRenderer mRenderer;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  public Vertex3DSurfaceView(Context c, AttributeSet attrs)
-    {
-    super(c, attrs);
-      
-    if(!isInEditMode())
-      {
-      setEGLContextClientVersion(2);
-        
-      if( Build.FINGERPRINT.startsWith("generic") )
-        {
-        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
-        }
-
-      mRenderer = new Vertex3DRenderer(this);
-
-      setRenderer(mRenderer);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Vertex3DRenderer getRenderer()
-    {
-    return mRenderer;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean onTouchEvent(MotionEvent event)
-    {
-    int action = event.getAction();
-    int x = (int)event.getX();
-    int y = (int)event.getY();
-
-    switch(action)
-      {
-      case MotionEvent.ACTION_DOWN: mX = x;
-                                    mY = y;
-                                    break;
-
-      case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
-                                      {
-                                      float px = mY-y;
-                                      float py = mX-x;
-                                      float pz = 0;
-                                      float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
-
-                                      if( plen>0 )
-                                        {
-                                        px /= plen;
-                                        py /= plen;
-                                        pz /= plen;
-
-                                        float cosA = (float)Math.cos(plen*3.14f/mRenderer.mScreenMin);
-                                        float sinA = (float)Math.sqrt(1-cosA*cosA);
-
-                                        mRenderer.mQuat1.set(px*sinA, py*sinA, pz*sinA, cosA);
-                                        }
-                                      }
-                                    break;
-
-      case MotionEvent.ACTION_UP  : mX = -1;
-                                    mY = -1;
-
-                                    float qx = mRenderer.mQuat1.getX();
-                                    float qy = mRenderer.mQuat1.getY();
-                                    float qz = mRenderer.mQuat1.getZ();
-                                    float qw = mRenderer.mQuat1.getW();
-
-                                    float rx = mRenderer.mQuat2.getX();
-                                    float ry = mRenderer.mQuat2.getY();
-                                    float rz = mRenderer.mQuat2.getZ();
-                                    float rw = mRenderer.mQuat2.getW();
-
-                                    float tx = rw*qx - rz*qy + ry*qz + rx*qw;
-                                    float ty = rw*qy + rz*qx + ry*qw - rx*qz;
-                                    float tz = rw*qz + rz*qw - ry*qx + rx*qy;
-                                    float tw = rw*qw - rz*qz - ry*qy - rx*qx;
-
-                                    mRenderer.mQuat1.set(0f, 0f, 0f, 1f);
-                                    mRenderer.mQuat2.set(tx, ty, tz, tw);
-
-                                    break;
-      }
-
-    return true;
-    }
-}
-
diff --git a/src/main/res/drawable-hdpi/icon_example_effects3d.png b/src/main/res/drawable-hdpi/icon_example_effects3d.png
new file mode 100644
index 0000000..a378eea
Binary files /dev/null and b/src/main/res/drawable-hdpi/icon_example_effects3d.png differ
diff --git a/src/main/res/drawable-hdpi/icon_example_fragment3d.png b/src/main/res/drawable-hdpi/icon_example_fragment3d.png
deleted file mode 100644
index 0f27e84..0000000
Binary files a/src/main/res/drawable-hdpi/icon_example_fragment3d.png and /dev/null differ
diff --git a/src/main/res/drawable-hdpi/icon_example_vertex3d.png b/src/main/res/drawable-hdpi/icon_example_vertex3d.png
deleted file mode 100644
index a378eea..0000000
Binary files a/src/main/res/drawable-hdpi/icon_example_vertex3d.png and /dev/null differ
diff --git a/src/main/res/layout/effectcenter.xml b/src/main/res/layout/effectcenter.xml
index fe92f3c..7524943 100644
--- a/src/main/res/layout/effectcenter.xml
+++ b/src/main/res/layout/effectcenter.xml
@@ -39,10 +39,4 @@
 
     </LinearLayout>
 
-    <View
-        android:layout_height="3dip"
-        android:background="#777777"
-        android:layout_width="match_parent"
-        />
-
 </LinearLayout>
diff --git a/src/main/res/layout/effectregion.xml b/src/main/res/layout/effectregion.xml
index 48681c3..27acadc 100644
--- a/src/main/res/layout/effectregion.xml
+++ b/src/main/res/layout/effectregion.xml
@@ -64,4 +64,10 @@
 
     </LinearLayout>
 
+    <View
+        android:layout_height="3dip"
+        android:background="#777777"
+        android:layout_width="match_parent"
+        />
+
 </LinearLayout>
diff --git a/src/main/res/layout/effects3dlayout.xml b/src/main/res/layout/effects3dlayout.xml
new file mode 100644
index 0000000..eb722aa
--- /dev/null
+++ b/src/main/res/layout/effects3dlayout.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <org.distorted.examples.effects3d.Effects3DSurfaceView
+        android:id="@+id/effects3dSurfaceView"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_gravity="center">
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/Add"
+            android:id="@+id/buttonAdd"
+            android:onClick="newEffect"
+            />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/New"
+            android:id="@+id/textView10"/>
+
+        <Spinner
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/effects3dspinner"
+            android:layout_weight="0.5"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/RemoveAll"
+            android:id="@+id/buttonRemove"
+            android:onClick="removeAll"
+            />
+
+    </LinearLayout>
+
+    <View
+        android:layout_height="4dip"
+        android:background="#777777"
+        android:layout_width="match_parent"
+        />
+
+    <ScrollView
+        android:id="@+id/effects3dscrollView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="0.82"
+        >
+
+        <LinearLayout
+            android:id="@+id/effects3dlayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+        </LinearLayout>
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/src/main/res/layout/fragment3dlayout.xml b/src/main/res/layout/fragment3dlayout.xml
deleted file mode 100644
index f9787f6..0000000
--- a/src/main/res/layout/fragment3dlayout.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical" >
-
-    <org.distorted.examples.fragment3d.Fragment3DSurfaceView
-        android:id="@+id/fragment3dSurfaceView"
-        android:layout_width="fill_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="50dp"
-        android:layout_gravity="center">
-
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/Add"
-            android:id="@+id/buttonAdd"
-            android:onClick="newEffect"
-            />
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:text="@string/New"
-            android:id="@+id/textView10"/>
-
-        <Spinner
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/fragment3dspinner"
-            android:layout_weight="0.5"/>
-
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/RemoveAll"
-            android:id="@+id/buttonRemove"
-            android:onClick="removeAll"
-            />
-
-    </LinearLayout>
-
-    <View
-        android:layout_height="4dip"
-        android:background="#777777"
-        android:layout_width="match_parent"
-        />
-
-    <ScrollView
-        android:id="@+id/fragment3dscrollView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.82"
-        >
-
-        <LinearLayout
-            android:id="@+id/fragment3dlayout"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical" >
-        </LinearLayout>
-
-    </ScrollView>
-
-</LinearLayout>
diff --git a/src/main/res/layout/vertex3dlayout.xml b/src/main/res/layout/vertex3dlayout.xml
deleted file mode 100644
index 3df32ea..0000000
--- a/src/main/res/layout/vertex3dlayout.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical" >
-
-    <org.distorted.examples.vertex3d.Vertex3DSurfaceView
-        android:id="@+id/vertex3dSurfaceView"
-        android:layout_width="fill_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="50dp"
-        android:layout_gravity="center">
-
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/Add"
-            android:id="@+id/buttonAdd"
-            android:onClick="newEffect"
-            />
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:text="@string/New"
-            android:id="@+id/textView10"/>
-
-        <Spinner
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/vertex3dspinner"
-            android:layout_weight="0.5"/>
-
-        <Button
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/RemoveAll"
-            android:id="@+id/buttonRemove"
-            android:onClick="removeAll"
-            />
-
-    </LinearLayout>
-
-    <View
-        android:layout_height="4dip"
-        android:background="#777777"
-        android:layout_width="match_parent"
-        />
-
-    <ScrollView
-        android:id="@+id/vertex3dscrollView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.82"
-        >
-
-        <LinearLayout
-            android:id="@+id/vertex3dlayout"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical" >
-        </LinearLayout>
-
-    </ScrollView>
-
-</LinearLayout>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index b57de8a..5f7685a 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -94,10 +94,8 @@
     <string name="example_quaternion_subtitle">Random rotations using quaternions.</string>
     <string name="example_matrix3d">Matrix Effects</string>
     <string name="example_matrix3d_subtitle">Test results of Matrix effects on a 3D object.</string>
-    <string name="example_vertex3d">Vertex Effects</string>
-    <string name="example_vertex3d_subtitle">Test results of Vertex effects on a 3D object.</string>
-    <string name="example_fragment3d">Fragment Effects</string>
-    <string name="example_fragment3d_subtitle">Test results of Fragment effects on a 3D object.</string>
+    <string name="example_effects3d">Vertex and Fragment effects</string>
+    <string name="example_effects3d_subtitle">Test results of Vertex and Fragment effects on a 3D object.</string>
     <string name="example_plainmonalisa">SurfaceView</string>
     <string name="example_plainmonalisa_subtitle">MonaLisa rendered on a plain SurfaceView</string>
     <string name="example_save">Save to PNG</string>
