commit 4d5b37fe56d2ab5ff5fa681f1d1f6aa9a35ce029
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Mon Nov 14 16:05:48 2016 +0000

    Unify Effects3D and Matrix3D (still incomplete!)

diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java b/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java
index a3deedf..a155d59 100644
--- a/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DActivity.java
@@ -77,7 +77,7 @@ public class Effects3DActivity extends Activity
   private EffectNames[] mEffectNames;
 
   private static boolean mSupportsRegion;
-  private static boolean mTypeVertex;
+  private static boolean mSupportsCenter;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -235,16 +235,16 @@ public class Effects3DActivity extends Activity
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static void setTypeVertex(boolean type)
+  public static void setSupportsCenter(boolean supports)
     {
-    mTypeVertex = type;
+    mSupportsCenter = supports;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static boolean isTypeVertex()
+  public static boolean supportsCenter()
     {
-    return mTypeVertex;
+    return mSupportsCenter;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -461,7 +461,7 @@ public class Effects3DActivity extends Activity
     mRegionR = getWidth()/2;
 
     mSupportsRegion = true;
-    mTypeVertex     =false;
+    mSupportsCenter =false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -500,7 +500,7 @@ public class Effects3DActivity extends Activity
     View view = eff.createView();
     layout.addView(view);
 
-    if( mTypeVertex )
+    if( mSupportsCenter )
       {
       View center = eff.createCenter();
       layout.addView(center);
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java b/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java
index f364586..7de4a91 100644
--- a/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DEffect.java
@@ -408,7 +408,7 @@ class Effects3DEffect implements SeekBar.OnSeekBarChangeListener
       }
 
     Effects3DActivity.setSupportsRegion(mName.supportsRegion());
-    Effects3DActivity.setTypeVertex(mName.getType() == EffectTypes.VERTEX);
+    Effects3DActivity.setSupportsCenter(mName.supportsCenter());
 
     return mEffect;
     }
@@ -555,7 +555,7 @@ class Effects3DEffect implements SeekBar.OnSeekBarChangeListener
     if( fromUser )
       {
       Effects3DActivity.setSupportsRegion(mName.supportsRegion());
-      Effects3DActivity.setTypeVertex(mName.getType() == EffectTypes.VERTEX);
+      Effects3DActivity.setSupportsCenter(mName.supportsCenter());
 
       Effects3DActivity act = mAct.get();
 
diff --git a/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java b/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java
index 4b419b1..f77f112 100644
--- a/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java
+++ b/src/main/java/org/distorted/examples/effects3d/Effects3DRenderer.java
@@ -126,7 +126,7 @@ class Effects3DRenderer implements GLSurfaceView.Renderer
       mBackground.draw(time);
       mObject.draw(time);
 
-      if( Effects3DActivity.isTypeVertex() )
+      if( Effects3DActivity.supportsCenter() )
         {
         mCenter.draw(time);
         if( Effects3DActivity.supportsRegion() ) mRegion.draw(time);
diff --git a/src/main/java/org/distorted/examples/matrix3d/Matrix3DActivity.java b/src/main/java/org/distorted/examples/matrix3d/Matrix3DActivity.java
index a6d636d..2aec555 100644
--- a/src/main/java/org/distorted/examples/matrix3d/Matrix3DActivity.java
+++ b/src/main/java/org/distorted/examples/matrix3d/Matrix3DActivity.java
@@ -19,367 +19,310 @@
 
 package org.distorted.examples.matrix3d;
 
-import org.distorted.library.Distorted;
-import org.distorted.examples.R;
-import org.distorted.library.EffectNames;
-
 import android.app.Activity;
-import android.os.Bundle;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.opengl.GLSurfaceView;
+import android.os.Bundle;
 import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.Spinner;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+import org.distorted.examples.R;
+import org.distorted.library.Distorted;
+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 Matrix3DActivity extends Activity implements OnSeekBarChangeListener
-{
-    private TextView textMove, textScale, textRotate, textShear;
-    private int moveX, moveY, moveZ;
-    private int scaleX, scaleY, scaleZ;
-    private int rotateX, rotateY, rotateZ, rotateA;
-    private int shearX, shearY, shearZ;
-    private int maxX, maxY, maxZ;
-    private float fmoveX, fmoveY, fmoveZ;
-    private float fscaleX, fscaleY, fscaleZ;
-    private float frotateX, frotateY, frotateZ, frotateA;
-    private float fshearX, fshearY, fshearZ;
-    
-    private EffectNames[] effects = new EffectNames[4];
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    protected void onCreate(Bundle icicle) 
-      {
-      super.onCreate(icicle);
-      setContentView(R.layout.matrix3dlayout);
-      Default(null);
-      }
+
+public class Matrix3DActivity extends Activity implements AdapterView.OnItemSelectedListener
+  {
+  private DistortedObject mObject;
+
+  private ArrayList<Matrix3DEffect> mEffects;
+  private int mEffectAdd;
+
+  private EffectNames[] mEffectNames;
+
+  private static boolean mSupportsCenter;
+  private float mCenterX, mCenterY;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void Default(View view)
-      {
-      effects[0] = EffectNames.MOVE;
-      effects[1] = EffectNames.SCALE;
-      effects[2] = EffectNames.ROTATE;
-      effects[3] = EffectNames.SHEAR;
-    
-      moveX = 50;
-      moveY = 50;
-      moveZ = 50;
-      
-      scaleX = 50;
-      scaleY = 50;
-      scaleZ = 50;
-      
-      rotateX = 100;
-      rotateY =  50;
-      rotateZ =  50;
-      rotateA =  50;
-      
-      shearX = 50;
-      shearY = 50;
-      shearZ = 50;
-      
-      addViews();
-      }
-    
+  @Override
+  protected void onCreate(Bundle savedState)
+    {
+    super.onCreate(savedState);
+
+    mEffects = new ArrayList<>();
+
+    createEffectNames();
+
+    mObject = new DistortedCubes( 1, "1", 100);
+
+    setEffectView();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    private void addViews()
-      {
-      LinearLayout layout = (LinearLayout)findViewById(R.id.matrix3dlayout);
-    
-      layout.removeAllViews();
-      
-      View move   = getLayoutInflater().inflate(R.layout.matrix3dmove, null);
-      View scale  = getLayoutInflater().inflate(R.layout.matrix3dscale, null);
-      View rotate = getLayoutInflater().inflate(R.layout.matrix3drotate, null);
-      View shear  = getLayoutInflater().inflate(R.layout.matrix3dshear, null);
-     
-      for( int i=effects.length-1 ; i>=0 ; i-- )
+
+  private void createEffectNames()
+    {
+    EffectTypes type = EffectTypes.MATRIX;
+
+    EffectNames[] names = EffectNames.values();
+
+    int numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type ) numEffects++;
+
+    mEffectNames = new EffectNames[numEffects];
+
+    numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type )
         {
-        switch(effects[i])
-          {
-          case MOVE  : layout.addView(move   , 0); break; 
-          case SCALE : layout.addView(scale  , 0); break;
-          case ROTATE: layout.addView(rotate , 0); break;
-          case SHEAR : layout.addView(shear  , 0); break;
-          }
+        mEffectNames[numEffects++] = names[i];
         }
-      
-      textMove  = (TextView)findViewById(R.id.matrix3dmoveText);
-      textScale = (TextView)findViewById(R.id.matrix3dscaleText);
-      textRotate= (TextView)findViewById(R.id.matrix3drotateText);
-      textShear = (TextView)findViewById(R.id.matrix3dshearText);
-     
-      setMoveText();
-      setScaleText();
-      setRotateText();
-      setShearText();
-      
-      setBar(R.id.matrix3dmoveBar1, moveX);
-      setBar(R.id.matrix3dmoveBar2, moveY);
-      setBar(R.id.matrix3dmoveBar3, moveZ);
-      
-      setBar(R.id.matrix3dscaleBar1, scaleX);
-      setBar(R.id.matrix3dscaleBar2, scaleY);
-      setBar(R.id.matrix3dscaleBar3, scaleZ);
-      
-      setBar(R.id.matrix3drotateBar1, rotateX);
-      setBar(R.id.matrix3drotateBar2, rotateY);
-      setBar(R.id.matrix3drotateBar3, rotateZ);
-      setBar(R.id.matrix3drotateBar4, rotateA);
-      
-      setBar(R.id.matrix3dshearBar1, shearX);
-      setBar(R.id.matrix3dshearBar2, shearY);
-      setBar(R.id.matrix3dshearBar3, shearZ);
-      
-      Matrix3DSurfaceView view = (Matrix3DSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.getRenderer().setOrder(effects);
-      }
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void moveUp(EffectNames name)
-      {
-      int len = effects.length-1;	
-      int index = -1;
-      
-      for(int i=0; i<=len; i++)	
-        {
-        if( effects[i]==name )
-          {
-          index=i;
-          break;
-          }
-        }
-      
-      if( index==0 )
-        {
-        for(int i=0; i<len; i++)
-          effects[i] = effects[i+1];
-    	
-        effects[len] = name;
-        }
-      else if( index>0 )
-        {
-        effects[index]   = effects[index-1];
-        effects[index-1] = name;
-        }
-      
-      addViews();
-      }
- 
+  public DistortedObject getObject()
+    {
+    return mObject;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void ButtonMove(View v)
-      {
-      moveUp(EffectNames.MOVE);
-      }
+  public void setRegion(float x, float y, float r)
+    {
+
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void ButtonScale(View v)
-      {
-      moveUp(EffectNames.SCALE);
-      }
-    
+  public void setCenter(float x, float y)
+    {
+    mCenterX = x;
+    mCenterY = y;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void ButtonRotate(View v)
-      {
-      moveUp(EffectNames.ROTATE);
-      }
+  public static void setSupportsRegion(boolean supports)
+    {
+
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public void ButtonShear(View v)
-      {
-      moveUp(EffectNames.SHEAR);
-      }
- 
+  public static boolean supportsRegion()
+    {
+    return false;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    private void setBar(int id, int value)
-      {
-      SeekBar bar = (SeekBar)findViewById(id);
-      bar.setOnSeekBarChangeListener(this); 
-      bar.setProgress(value);
-      }
+
+  public static void setSupportsCenter(boolean supports)
+    {
+    mSupportsCenter = supports;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void computeMove()
-      {
-      fmoveX = (moveX-50)*maxX/50.0f;
-      fmoveY = (moveY-50)*maxY/50.0f;
-      fmoveZ = (moveZ-50)*maxZ/50.0f;
+  public static boolean supportsCenter()
+    {
+    return mSupportsCenter;
+    }
 
-      Matrix3DSurfaceView view = (Matrix3DSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.getRenderer().setMove( fmoveX, fmoveY, fmoveZ);
-      }
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void setMoveText()
+  public Bitmap getBitmap()
+    {
+    Bitmap bmp;
+
+    InputStream is = getResources().openRawResource(R.raw.grid);
+
+    try
+      {
+      bmp = BitmapFactory.decodeStream(is);
+      }
+    finally
       {
-      fmoveX = ((int)(100*fmoveX))/100.0f;
-      fmoveY = ((int)(100*fmoveY))/100.0f;
-      fmoveZ = ((int)(100*fmoveZ))/100.0f;
-      
-      textMove.setText("move("+fmoveX+" , "+fmoveY+" , "+fmoveZ+")");
+      try
+        {
+        is.close();
+        }
+      catch(IOException e) { }
       }
 
+    return bmp;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void computeScale()
-      {
-      fscaleX = (scaleX>50 ? 0.18f : 0.018f)*(scaleX-50)+1;
-      fscaleY = (scaleY>50 ? 0.18f : 0.018f)*(scaleY-50)+1;
-      fscaleZ = (scaleZ>50 ? 0.18f : 0.018f)*(scaleZ-50)+1;
+  public int getWidth()
+    {
+    return mObject==null ? 0: mObject.getWidth();
+    }
 
-      Matrix3DSurfaceView view = (Matrix3DSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.getRenderer().setScale(fscaleX, fscaleY, fscaleZ);
-      }
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void setScaleText()
-      {
-      fscaleX = ((int)(100*fscaleX))/100.0f;
-      fscaleY = ((int)(100*fscaleY))/100.0f;
-      fscaleZ = ((int)(100*fscaleZ))/100.0f;
-      
-      textScale.setText("scale("+fscaleX+" , "+fscaleY+" , "+fscaleZ+")");
-      }
+  public int getHeight()
+    {
+    return mObject==null ? 0: mObject.getHeight();
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void computeRotate()
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    switch(parent.getId())
       {
-      frotateX = (rotateX-50)/50.0f;
-      frotateY = (rotateY-50)/50.0f;
-      frotateZ = (rotateZ-50)/50.0f;
-    
-      Matrix3DSurfaceView view = (Matrix3DSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.getRenderer().setRotate( frotateA, frotateX, frotateY, frotateZ );
+      case R.id.matrix3dspinner          : mEffectAdd = pos;
+                                           break;
       }
-    
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void setRotateText()
-      {
-      frotateX = ((int)(100*frotateX))/100.0f;
-      frotateY = ((int)(100*frotateY))/100.0f;
-      frotateZ = ((int)(100*frotateZ))/100.0f;
-      
-      frotateA = ((rotateA-50)*180)/50;
-      
-      textRotate.setText("rotate( "+frotateA+" ("+frotateX+","+frotateY+","+frotateZ+") )");
-      }
+  public void onNothingSelected(AdapterView<?> parent)
+    {
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void computeShear()
-      {
-      fshearX = (shearX-50)/25.0f;
-      fshearY = (shearY-50)/25.0f;
-      fshearZ = (shearZ-50)/25.0f;
+  private void resetData()
+    {
+    mCenterX = 0.5f*getWidth();
+    mCenterY = 0.5f*getHeight();
 
-    	Matrix3DSurfaceView view = (Matrix3DSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.getRenderer().setShear( fshearX, fshearY, fshearZ );
-      }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    mSupportsCenter=false;
+    }
 
-    private void setShearText()
-      {
-      fshearX = ((int)(100*fshearX))/100.0f;
-      fshearY = ((int)(100*fshearY))/100.0f;
-      fshearZ = ((int)(100*fshearZ))/100.0f;
-      
-      textShear.setText("shear("+fshearX+" , "+fshearY+" , "+fshearZ+")");	
-      }
-   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    protected void onPause() 
-      {
-      GLSurfaceView view = (GLSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.onPause();
-      super.onPause();
-      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    protected void onResume() 
-      {
-      super.onResume();
-      GLSurfaceView view = (GLSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      view.onResume();
-      }
+  private void setEffectView()
+    {
+    resetData();
+
+    final View view = getLayoutInflater().inflate(R.layout.matrix3dlayout, 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.matrix3dspinner );
+    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;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) 
+
+  public void newEffect(View v)
+    {
+    Matrix3DEffect eff = new Matrix3DEffect(mEffectNames[mEffectAdd], this);
+    mEffects.add(eff);
+
+    LinearLayout layout = (LinearLayout)findViewById(R.id.matrix3dlayout);
+    View view = eff.createView();
+    layout.addView(view);
+
+    if( mSupportsCenter )
       {
-      super.onWindowFocusChanged(hasFocus);
-  
-      GLSurfaceView view = (GLSurfaceView) this.findViewById(R.id.matrix3dSurfaceView);
-      
-      maxX = view.getWidth();
-      maxY = view.getHeight();
-      maxZ = (maxX+maxY)/2;
+      View center = eff.createCenter();
+      layout.addView(center);
       }
 
+    eff.apply(mObject);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    protected void onDestroy() 
-      {
-      Distorted.onDestroy();  
-      super.onDestroy();
-      }
-    
+
+  public void removeAll(View v)
+    {
+    mEffects.clear();
+    LinearLayout layout = (LinearLayout)findViewById(R.id.matrix3dlayout);
+    layout.removeAllViews();
+    mObject.abortEffects(EffectTypes.MATRIX);
+
+    resetData();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) 
+
+  public void remove(View v)
+    {
+    for(Matrix3DEffect effect: mEffects)
       {
-      switch (bar.getId()) 
+      if( effect.thisView(v) )
         {
-        case R.id.matrix3dmoveBar1  : moveX = progress; computeMove()  ; setMoveText()  ; break;
-        case R.id.matrix3dmoveBar2  : moveY = progress; computeMove()  ; setMoveText()  ; break;
-        case R.id.matrix3dmoveBar3  : moveZ = progress; computeMove()  ; setMoveText()  ; break;
-        
-        case R.id.matrix3dscaleBar1 : scaleX= progress; computeScale() ; setScaleText() ; break;
-        case R.id.matrix3dscaleBar2 : scaleY= progress; computeScale() ; setScaleText() ; break;
-        case R.id.matrix3dscaleBar3 : scaleZ= progress; computeScale() ; setScaleText() ; break;
-        
-        case R.id.matrix3drotateBar1: rotateX=progress; computeRotate(); setRotateText(); break;
-        case R.id.matrix3drotateBar2: rotateY=progress; computeRotate(); setRotateText(); break;
-        case R.id.matrix3drotateBar3: rotateZ=progress; computeRotate(); setRotateText(); break;
-        case R.id.matrix3drotateBar4: rotateA=progress; computeRotate(); setRotateText(); break;
-        
-        case R.id.matrix3dshearBar1 : shearX= progress; computeShear() ; setShearText() ; break;
-        case R.id.matrix3dshearBar2 : shearY= progress; computeShear() ; setShearText() ; break;
-        case R.id.matrix3dshearBar3 : shearZ= progress; computeShear() ; setShearText() ; break;
+        LinearLayout layout = (LinearLayout)findViewById(R.id.matrix3dlayout);
+        View view;
+
+        view = effect.getEffect();
+        if( view!=null ) layout.removeView(view);
+        view = effect.getCenter();
+        if( view!=null ) layout.removeView(view);
+        view = effect.getRegion();
+        if( view!=null ) layout.removeView(view);
+
+        long id = effect.getId();
+        mObject.abortEffect(id);
+        mEffects.remove(effect);
+
+        resetData();
+
+        break;
         }
       }
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// Overrides
+
+  @Override
+  protected void onPause()
+    {
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.matrix3dSurfaceView);
+    if( mView!=null ) mView.onPause();
+    super.onPause();
+    }
 
-    public void onStartTrackingTouch(SeekBar bar) { }
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onResume()
+    {
+    super.onResume();
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.matrix3dSurfaceView);
+    if( mView!=null ) mView.onResume();
+    }
 
-    public void onStopTrackingTouch(SeekBar bar)  { }
+///////////////////////////////////////////////////////////////////////////////////////////////////
     
-}
+  @Override
+  protected void onDestroy()
+    {
+    Distorted.onDestroy();
+    super.onDestroy();
+    }
+
+  }
diff --git a/src/main/java/org/distorted/examples/matrix3d/Matrix3DEffect.java b/src/main/java/org/distorted/examples/matrix3d/Matrix3DEffect.java
new file mode 100644
index 0000000..823970e
--- /dev/null
+++ b/src/main/java/org/distorted/examples/matrix3d/Matrix3DEffect.java
@@ -0,0 +1,632 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.matrix3d;
+
+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.Dynamic5D;
+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 org.distorted.library.type.Static5D;
+
+import java.lang.ref.WeakReference;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Matrix3DEffect implements SeekBar.OnSeekBarChangeListener
+  {
+  private WeakReference<Matrix3DActivity> 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 Dynamic2D mDyn2;
+  private Dynamic3D mDyn3;
+  private Dynamic5D mDyn5;
+  private Static1D  mSta1;
+  private Static2D  mSta2;
+  private Static3D  mSta3;
+  private Static5D  mSta5;
+  private Dynamic4D mRegionDyn;
+  private Static4D  mRegionSta;
+  private Dynamic2D mCenterDyn;
+  private Static2D  mCenterSta;
+
+  private View mButton, mEffect, mCenter, mRegion;
+  private long mId;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  void apply(DistortedObject object)
+    {
+    switch(mName)
+      {
+      case ROTATE           : break;//object.rotate    (mDyn4, mCenterDyn); break;
+      case QUATERNION       : break;//object.quaternion(mDyn4, mCenterDyn); break;
+      case MOVE             : object.move      (mDyn3)            ; break;
+      case SCALE            : object.scale     (mDyn3)            ; break;
+      case SHEAR            : break;//object.shear     (mDyn3, mCenterDyn); break;
+
+      case DISTORT: mId = object.distort(mDyn3, mCenterDyn, mRegionDyn); break;
+      case DEFORM : mId = object.deform (mDyn3, mCenterDyn            ); break;
+      case SINK   : mId = object.sink   (mDyn1, mCenterDyn, mRegionDyn); break;
+      case SWIRL  : mId = object.swirl  (mDyn1, mCenterDyn, mRegionDyn); break;
+      case WAVE   : mId = object.wave   (mDyn5, mCenterDyn, mRegionDyn); break;
+
+      case ALPHA            : mId = object.alpha     (mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_ALPHA     : mId = object.alpha     (mDyn1,        mRegionDyn, true ); break;
+      case CHROMA           : mId = object.chroma    (mDyn1, mDyn3, mRegionDyn, false); break;
+      case SMOOTH_CHROMA    : mId = object.chroma    (mDyn1, mDyn3, mRegionDyn, true ); break;
+      case BRIGHTNESS       : mId = object.brightness(mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_BRIGHTNESS: mId = object.brightness(mDyn1,        mRegionDyn, true ); break;
+      case SATURATION       : mId = object.saturation(mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_SATURATION: mId = object.saturation(mDyn1,        mRegionDyn, true ); break;
+      case CONTRAST         : mId = object.contrast  (mDyn1,        mRegionDyn, false); break;
+      case SMOOTH_CONTRAST  : mId = object.contrast  (mDyn1,        mRegionDyn, true ); break;
+
+      default: mId = -1;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void fillStatics()
+    {
+    switch(mName)
+      {
+      case ROTATE           :
+                              break;
+      case QUATERNION       :
+                              break;
+      case MOVE             : float lm = mAct.get().getWidth()/50.0f;
+                              float xm = (mInter[0]-50)*lm;
+                              float ym = (mInter[1]-50)*lm;
+                              float zm = (mInter[2]-50)*lm;
+                              mSta3.set(xm,ym,zm);
+                              break;
+      case SCALE            : float xs = (mInter[0])/50.0f;
+                              float ys = (mInter[1])/50.0f;
+                              float zs = (mInter[2])/50.0f;
+                              mSta3.set(xs,ys,zs);
+                              break;
+      case SHEAR            :
+                              break;
+      case DISTORT          :
+      case DEFORM           : float ld = mAct.get().getWidth()/50.0f;
+                              float xd = (mInter[0]-50)*ld;
+                              float yd = (mInter[1]-50)*ld;
+                              float zd = (mInter[2]-50)*ld;
+                              mSta3.set(xd,yd,zd);
+                              break;
+      case WAVE             : float l2 = mAct.get().getWidth()/50.0f;
+                              float x2 = (mInter[0]-50)*l2;
+                              float y2 = (mInter[1]-50)*l2;
+                              float z2 = (mInter[2]-50)*180 / 50;
+                              float w2 = (mInter[3]-50)*180 / 50;
+                              float v2 = (mInter[4]-50)*180 / 50;
+                              mSta5.set(x2,y2,z2,w2,v2);
+                              break;
+      case SWIRL            : mSta1.set( 3.6f*(mInter[0]-50) );
+                              break;
+      case ALPHA            :
+      case SMOOTH_ALPHA     : mSta1.set(mInter[0]/100.0f);
+                              break;
+      case SINK             :
+      case SATURATION       :
+      case SMOOTH_SATURATION:
+      case CONTRAST         :
+      case SMOOTH_CONTRAST  :
+      case BRIGHTNESS       :
+      case SMOOTH_BRIGHTNESS: mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
+                              break;
+      case CHROMA           :
+      case SMOOTH_CHROMA    : mSta1.set(mInter[0]/100.0f);
+                              mSta3.set(mInter[1]/100.0f,
+                                        mInter[2]/100.0f,
+                                        mInter[3]/100.0f);
+                              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultInter()
+    {
+    switch(mDimension)
+      {
+      case 5: mInter[4] = 50;
+      case 4: mInter[3] = 50;
+      case 3: mInter[2] = 50;
+      case 2: mInter[1] = 50;
+      case 1: mInter[0] = 50;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setText()
+    {
+    String text = mName.name();
+
+    if( mSta1 !=null )
+      {
+      float f1 = ((int)(mSta1.getX()*100))/100.0f;
+      text += " "+f1;
+      }
+
+    if( mSta2 !=null )
+      {
+      float f1 = ((int)(mSta2.getX()*100))/100.0f;
+      float f2 = ((int)(mSta2.getY()*100))/100.0f;
+      text += " ("+f1+","+f2+")";
+      }
+
+    if( mSta3 !=null )
+      {
+      float f1 = ((int)(mSta3.getX()*100))/100.0f;
+      float f2 = ((int)(mSta3.getY()*100))/100.0f;
+      float f3 = ((int)(mSta3.getZ()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+")";
+      }
+
+    if( mSta5 !=null )
+      {
+      float f1 = ((int)(mSta5.getX()*100))/100.0f;
+      float f2 = ((int)(mSta5.getY()*100))/100.0f;
+      float f3 = ((int)(mSta5.getZ()*100))/100.0f;
+      float f4 = ((int)(mSta5.getW()*100))/100.0f;
+      float f5 = ((int)(mSta5.getV()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+","+f4+","+f5+")";
+      }
+
+    mText.setText(text);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillCenterStatics()
+    {
+    Matrix3DActivity 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()
+    {
+    Matrix3DActivity 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+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Matrix3DEffect(EffectNames name, Matrix3DActivity act)
+    {
+    mAct = new WeakReference<>(act);
+    mName = name;
+
+    mDyn1 = null;
+    mDyn2 = null;
+    mDyn3 = null;
+    mDyn5 = null;
+    mSta1 = null;
+    mSta2 = null;
+    mSta3 = null;
+    mSta5 = null;
+
+    mDimension = mName.getDimension();
+
+    switch(mDimension)
+      {
+      case 1 : mDyn1 = new Dynamic1D();
+               mSta1 = new Static1D(0);
+               mDyn1.add(mSta1);
+               break;
+      case 2 : mDyn2 = new Dynamic2D();
+               mSta2 = new Static2D(0,0);
+               mDyn2.add(mSta2);
+               break;
+      case 3 : mDyn3 = new Dynamic3D();
+               mSta3 = new Static3D(0,0,0);
+               mDyn3.add(mSta3);
+               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;
+      case 5 : mDyn5 = new Dynamic5D();
+               mSta5 = new Static5D(0,0,0,0,0);
+               mDyn5.add(mSta5);
+               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);
+
+    mButton = null;
+    mEffect = null;
+    mCenter = null;
+    mRegion = null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createView()
+    {
+    SeekBar[] seek = new SeekBar[mDimension];
+
+    Matrix3DActivity act = mAct.get();
+
+    switch(mDimension)
+      {
+      case 1 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect1d, null);
+               mText      = (TextView)mEffect.findViewById(R.id.effect1dText);
+               seek[0]    = (SeekBar)mEffect.findViewById(R.id.effect1dbar1);
+               mSeekID[0] = seek[0].getId();
+               mButton    = mEffect.findViewById(R.id.button1dRemove);
+               break;
+      case 2 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect2d, null);
+               mText      = (TextView)mEffect.findViewById(R.id.effect2dText);
+               seek[0]    = (SeekBar)mEffect.findViewById(R.id.effect2dbar1);
+               seek[1]    = (SeekBar)mEffect.findViewById(R.id.effect2dbar2);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mButton    = mEffect.findViewById(R.id.button2dRemove);
+               break;
+      case 3 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect3d, null);
+               mText      = (TextView)mEffect.findViewById(R.id.effect3dText);
+               seek[0]    = (SeekBar)mEffect.findViewById(R.id.effect3dbar1);
+               seek[1]    = (SeekBar)mEffect.findViewById(R.id.effect3dbar2);
+               seek[2]    = (SeekBar)mEffect.findViewById(R.id.effect3dbar3);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mButton    = mEffect.findViewById(R.id.button3dRemove);
+               break;
+      case 4 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect4d, null);
+               mText      = (TextView)mEffect.findViewById(R.id.effect4dText);
+               seek[0]    = (SeekBar)mEffect.findViewById(R.id.effect4dbar1);
+               seek[1]    = (SeekBar)mEffect.findViewById(R.id.effect4dbar2);
+               seek[2]    = (SeekBar)mEffect.findViewById(R.id.effect4dbar3);
+               seek[3]    = (SeekBar)mEffect.findViewById(R.id.effect4dbar4);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mSeekID[3] = seek[3].getId();
+               mButton    = mEffect.findViewById(R.id.button4dRemove);
+               break;
+      case 5 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect5d, null);
+               mText      = (TextView)mEffect.findViewById(R.id.effect5dText);
+               seek[0]    = (SeekBar)mEffect.findViewById(R.id.effect5dbar1);
+               seek[1]    = (SeekBar)mEffect.findViewById(R.id.effect5dbar2);
+               seek[2]    = (SeekBar)mEffect.findViewById(R.id.effect5dbar3);
+               seek[3]    = (SeekBar)mEffect.findViewById(R.id.effect5dbar4);
+               seek[4]    = (SeekBar)mEffect.findViewById(R.id.effect5dbar5);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mSeekID[3] = seek[3].getId();
+               mSeekID[4] = seek[4].getId();
+               mButton    = mEffect.findViewById(R.id.button5dRemove);
+               break;
+      default: android.util.Log.e("Matrix3DEffect", "dimension "+mDimension+" not supported!");
+               return null;
+      }
+
+    setDefaultInter();
+
+    for(int i=0; i<mDimension; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInter[i] );
+      }
+
+    Matrix3DActivity.setSupportsRegion(mName.supportsRegion());
+    Matrix3DActivity.setSupportsCenter(mName.supportsCenter());
+
+    return mEffect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createRegion()
+    {
+    Matrix3DActivity act = mAct.get();
+
+    mRegion = act.getLayoutInflater().inflate(R.layout.effectregion, null);
+
+    SeekBar[] seek = new SeekBar[4];
+
+    seek[0] = (SeekBar)mRegion.findViewById(R.id.effectRegionBarX );
+    seek[1] = (SeekBar)mRegion.findViewById(R.id.effectRegionBarY );
+    seek[2] = (SeekBar)mRegion.findViewById(R.id.effectRegionBarRX);
+    seek[3] = (SeekBar)mRegion.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)mRegion.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 mRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createCenter()
+    {
+    Matrix3DActivity act = mAct.get();
+
+    mCenter = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
+
+    SeekBar[] seek = new SeekBar[2];
+
+    seek[0] = (SeekBar)mCenter.findViewById(R.id.effectCenterBarX );
+    seek[1] = (SeekBar)mCenter.findViewById(R.id.effectCenterBarY );
+
+    mSeekCenterID[0] = seek[0].getId();
+    mSeekCenterID[1] = seek[1].getId();
+
+    mTextCenter = (TextView)mCenter.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 mCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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 ( mDimension>=5 && bar.getId()==mSeekID[4] )
+      {
+      mInter[4] = 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 )
+      {
+      Matrix3DActivity.setSupportsRegion(mName.supportsRegion());
+      Matrix3DActivity.setSupportsCenter(mName.supportsCenter());
+
+      Matrix3DActivity act = mAct.get();
+
+      act.setCenter(mCenterSta.getX(),mCenterSta.getY());
+      act.setRegion(mRegionSta.getX(),mRegionSta.getY(),mRegionSta.getZ());
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean thisView(View v)
+    {
+    return v==mButton;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getId()
+    {
+    return mId;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getEffect()
+    {
+    return mEffect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getRegion()
+    {
+    return mRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getCenter()
+    {
+    return mCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStartTrackingTouch(SeekBar bar) { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/examples/matrix3d/Matrix3DRenderer.java b/src/main/java/org/distorted/examples/matrix3d/Matrix3DRenderer.java
index 9ee66a0..23b1147 100644
--- a/src/main/java/org/distorted/examples/matrix3d/Matrix3DRenderer.java
+++ b/src/main/java/org/distorted/examples/matrix3d/Matrix3DRenderer.java
@@ -19,162 +19,62 @@
 
 package org.distorted.examples.matrix3d;
 
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-import org.distorted.examples.R;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
 
-import org.distorted.library.DistortedCubes;
-import org.distorted.library.EffectNames;
-import org.distorted.library.EffectTypes;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-import org.distorted.library.type.Dynamic4D;
 import org.distorted.library.Distorted;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectTypes;
 
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class Matrix3DRenderer implements GLSurfaceView.Renderer
 {
-    private static final int SIZE = 100;
-	
     private GLSurfaceView mView;
-    private DistortedCubes mCube;
-    private EffectNames[] order;
-    
-    private Dynamic3D mMoveInter, mScaleInter, mShearInter;
-    private Dynamic4D mDynamicRotate;
-    private Static3D mZeroPoint, mMovePoint, mScalePoint, mShearPoint;
-    private Static4D mRotatePoint;
+    private DistortedObject mObject;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void setMove(float x, float y, float z)
+    Matrix3DRenderer(GLSurfaceView v)
       {
-      mMovePoint.set(x, y, z);
-      }
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
+      mView = v;
 
-    void setScale(float x, float y, float z)
-      {
-      mScalePoint.set(x, y, z);
+      mObject= ((Matrix3DActivity)v.getContext()).getObject();
       }
-     
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void setRotate(float a, float x, float y, float z)
-      {
-      mRotatePoint.set(a,x,y,z);
-      }
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    void setShear(float x, float y, float z)
+    public void onDrawFrame(GL10 glUnused)
       {
-      mShearPoint.set(x, y, z);
-      }
+      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+      long time = System.currentTimeMillis();
 
-    void setOrder(EffectNames[] effects)
-      {
-      order = effects;
-      setMatrixEffects();
+      mObject.draw(time);
       }
-      
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private void setMatrixEffects()
-      {
-      mCube.abortEffects(EffectTypes.MATRIX);
-	
-      for( int i=0; i<=order.length-1 ; i++ )
-        {
-        switch(order[i])
-          {
-          case MOVE  : mCube.move(mMoveInter)                 ; break;
-          case SCALE : mCube.scale(mScaleInter)               ; break;
-          case ROTATE: mCube.rotate(mDynamicRotate,mZeroPoint); break;
-          case SHEAR : mCube.shear(mShearInter, mZeroPoint)   ; break;
-          }
-        }
-      }
-    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    Matrix3DRenderer(GLSurfaceView v)
+    public void onSurfaceChanged(GL10 glUnused, int width, int height)
       {
-      mView = v;
-      mCube = new DistortedCubes( 1, "1", SIZE);
-      
-      mZeroPoint   = new Static3D(0,0,0);
-      mMovePoint   = new Static3D(0,0,0);
-      mScalePoint  = new Static3D(1,1,1);
-      mShearPoint  = new Static3D(0,0,0);
-      mRotatePoint = new Static4D(0,1,0,0);
-
-      mMoveInter    = new Dynamic3D();
-      mScaleInter   = new Dynamic3D();
-      mShearInter   = new Dynamic3D();
-      mDynamicRotate= new Dynamic4D();
-
-      mMoveInter.add(mMovePoint);
-      mScaleInter.add(mScalePoint);
-      mShearInter.add(mShearPoint);
-      mDynamicRotate.add(mRotatePoint);
-      }
+      mObject.abortEffects(EffectTypes.MATRIX);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-    public void onDrawFrame(GL10 glUnused) 
-      {
-      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-      mCube.draw(System.currentTimeMillis());
+      Distorted.onSurfaceChanged(width, height);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    public void onSurfaceChanged(GL10 glUnused, int width, int height) 
-      {
-      setMatrixEffects();
-
-      Distorted.onSurfaceChanged(width, height); 
-      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
+    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
       {
       GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 
-      InputStream is = mView.getContext().getResources().openRawResource(R.raw.grid);
-      Bitmap bitmap;
-        
-      try 
-        {
-        bitmap = BitmapFactory.decodeStream(is);
-        } 
-      finally 
-        {
-        try 
-          {
-          is.close();
-          } 
-        catch(IOException e) { }
-        }  
-      
-      mCube.setBitmap(bitmap);
-      
+      Matrix3DActivity act = (Matrix3DActivity)mView.getContext();
+
+      mObject.setBitmap( act.getBitmap() );
+
       try
         {
         Distorted.onSurfaceCreated(mView.getContext());
@@ -184,4 +84,4 @@ class Matrix3DRenderer implements GLSurfaceView.Renderer
         android.util.Log.e("Matrix3D", ex.getMessage() );
         }
       }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/matrix3d/Matrix3DSurfaceView.java b/src/main/java/org/distorted/examples/matrix3d/Matrix3DSurfaceView.java
index 9196b37..9092a23 100644
--- a/src/main/java/org/distorted/examples/matrix3d/Matrix3DSurfaceView.java
+++ b/src/main/java/org/distorted/examples/matrix3d/Matrix3DSurfaceView.java
@@ -23,39 +23,41 @@ import android.content.Context;
 import android.opengl.GLSurfaceView;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class Matrix3DSurfaceView extends GLSurfaceView
-{
-    private Matrix3DRenderer mRenderer;
+  {
+  private Matrix3DRenderer mRenderer;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-    public Matrix3DSurfaceView(Context c, AttributeSet attrs)
-      {
-      super(c, attrs);
+  public Matrix3DSurfaceView(Context c, AttributeSet attrs)
+    {
+    super(c, attrs);
       
-      if(!isInEditMode())
-        {
-        setEGLContextClientVersion(2);
+    if(!isInEditMode())
+      {
+      setEGLContextClientVersion(2);
         
-        if( Build.FINGERPRINT.startsWith("generic") )
-          { 
-          setEGLConfigChooser(8, 8, 8, 8, 16, 0);   
-          }
+      if( Build.FINGERPRINT.startsWith("generic") )
+        {
+        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
+        }
 
-        mRenderer = new Matrix3DRenderer(this);
+      mRenderer = new Matrix3DRenderer(this);
 
-        setRenderer(mRenderer);
-        }
+      setRenderer(mRenderer);
       }
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    Matrix3DRenderer getRenderer()
-      {
-      return mRenderer;
-      }
+  public Matrix3DRenderer getRenderer()
+    {
+    return mRenderer;
+    }
+
 }
 
diff --git a/src/main/res/layout/matrix3dlayout.xml b/src/main/res/layout/matrix3dlayout.xml
index aad37e1..22beee0 100644
--- a/src/main/res/layout/matrix3dlayout.xml
+++ b/src/main/res/layout/matrix3dlayout.xml
@@ -10,18 +10,55 @@
         android:layout_height="0dp"
         android:layout_weight="1" />
 
-    <Button
-        android:id="@+id/button1"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:onClick="Default"
-        android:text="@string/reset" />
+    <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/matrix3dspinner"
+            android:layout_weight="0.5"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/reset"
+            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/matrix3dscrollView"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="0.82" >
+        android:layout_weight="0.82"
+        >
 
         <LinearLayout
             android:id="@+id/matrix3dlayout"
diff --git a/src/main/res/layout/matrix3dmove.xml b/src/main/res/layout/matrix3dmove.xml
deleted file mode 100644
index cbeaf8f..0000000
--- a/src/main/res/layout/matrix3dmove.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:id="@+id/matrix3dRowMov"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal" >
-
-                <LinearLayout
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="0.8"
-                    android:orientation="vertical" >
-
-                    <TextView
-                        android:id="@+id/matrix3dmoveText"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginStart="5dp"
-                        android:layout_marginTop="3dp"
-                        android:textAppearance="?android:attr/textAppearanceLarge" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dmoveBar1"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dmoveBar2"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dmoveBar3"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                </LinearLayout>
-
-                <Button
-                    android:id="@+id/matrix3dUpMove"
-                    android:layout_width="60dp"
-                    android:layout_height="fill_parent"
-                    android:onClick="ButtonMove"
-                    android:text="@string/Up" />
-
-            </LinearLayout>
diff --git a/src/main/res/layout/matrix3drotate.xml b/src/main/res/layout/matrix3drotate.xml
deleted file mode 100644
index df898da..0000000
--- a/src/main/res/layout/matrix3drotate.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:id="@+id/matrix3dRowRotate"
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" >
-
-                <LinearLayout
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="0.70"
-                    android:orientation="vertical" >
-
-                    <TextView
-                        android:id="@+id/matrix3drotateText"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginStart="5dp"
-                        android:layout_marginTop="3dp"
-                        android:textAppearance="?android:attr/textAppearanceLarge" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3drotateBar4"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3drotateBar1"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3drotateBar2"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3drotateBar3"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                </LinearLayout>
-
-                <Button
-                    android:id="@+id/matrix3dUpRotate"
-                    android:layout_width="60dp"
-                    android:layout_height="fill_parent"
-                    android:onClick="ButtonRotate"
-                    android:text="@string/Up" />
-
-            </LinearLayout>
\ No newline at end of file
diff --git a/src/main/res/layout/matrix3dscale.xml b/src/main/res/layout/matrix3dscale.xml
deleted file mode 100644
index 3c91178..0000000
--- a/src/main/res/layout/matrix3dscale.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:id="@+id/matrix3dRowScale"
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" >
-
-                <LinearLayout
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="0.70"
-                    android:orientation="vertical" >
-
-                    <TextView
-                        android:id="@+id/matrix3dscaleText"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginStart="5dp"
-                        android:layout_marginTop="3dp"
-                        android:textAppearance="?android:attr/textAppearanceLarge" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dscaleBar1"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dscaleBar2"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dscaleBar3"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                </LinearLayout>
-
-                <Button
-                    android:id="@+id/matrix3dUpScale"
-                    android:layout_width="60dp"
-                    android:layout_height="fill_parent"
-                    android:onClick="ButtonScale"
-                    android:text="@string/Up" />
-
-            </LinearLayout>
-           
\ No newline at end of file
diff --git a/src/main/res/layout/matrix3dshear.xml b/src/main/res/layout/matrix3dshear.xml
deleted file mode 100644
index 236b462..0000000
--- a/src/main/res/layout/matrix3dshear.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:id="@+id/matrix3dRowShear"
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" >
-
-                <LinearLayout
-                    android:layout_width="0dp"
-                    android:layout_height="match_parent"
-                    android:layout_weight="0.70"
-                    android:orientation="vertical" >
-
-                    <TextView
-                        android:id="@+id/matrix3dshearText"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginStart="5dp"
-                        android:layout_marginTop="3dp"
-                        android:textAppearance="?android:attr/textAppearanceLarge" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dshearBar1"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dshearBar2"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                    <SeekBar
-                        android:id="@+id/matrix3dshearBar3"
-                        android:layout_width="fill_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="5dp"
-                        android:layout_marginLeft="5dp"
-                        android:layout_marginRight="5dp" />
-
-                </LinearLayout>
-
-                <Button
-                    android:id="@+id/matrix3dUpShear"
-                    android:layout_width="60dp"
-                    android:layout_height="fill_parent"
-                    android:onClick="ButtonShear"
-                    android:text="@string/Up" />
-
-            </LinearLayout>
\ No newline at end of file
