commit 9df01f210b80f5abf133b9bda4fb17e195a8682a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon May 18 00:46:55 2020 +0100

    Progress with Predeform: UI for the list of Vertex effects to preapply.

diff --git a/src/main/java/org/distorted/examples/earth/EarthActivity.java b/src/main/java/org/distorted/examples/earth/EarthActivity.java
index 292315b..a4427ef 100644
--- a/src/main/java/org/distorted/examples/earth/EarthActivity.java
+++ b/src/main/java/org/distorted/examples/earth/EarthActivity.java
@@ -172,9 +172,9 @@ public class EarthActivity extends Activity implements SeekBar.OnSeekBarChangeLi
 
       int numEffects=0;
 
-      for(int i=0; i<names.length; i++)
+      for (EffectName effectName : names)
         {
-        type = names[i].getType();
+        type = effectName.getType();
         if (type == EffectType.FRAGMENT || type == EffectType.VERTEX) numEffects++;
         }
 
@@ -183,14 +183,14 @@ public class EarthActivity extends Activity implements SeekBar.OnSeekBarChangeLi
 
       numEffects=0;
 
-      for(int i=0; i<names.length; i++)
+      for (EffectName name : names)
         {
-        type = names[i].getType();
+        type = name.getType();
 
         if (type == EffectType.FRAGMENT || type == EffectType.VERTEX)
           {
-          mEffectNames[numEffects]  = names[i];
-          effectStrings[numEffects] = names[i].name();
+          mEffectNames[numEffects] = name;
+          effectStrings[numEffects] = name.name();
 
           numEffects++;
           }
diff --git a/src/main/java/org/distorted/examples/predeform/PredeformActivity.java b/src/main/java/org/distorted/examples/predeform/PredeformActivity.java
index 7892524..485d2ac 100644
--- a/src/main/java/org/distorted/examples/predeform/PredeformActivity.java
+++ b/src/main/java/org/distorted/examples/predeform/PredeformActivity.java
@@ -22,7 +22,6 @@ package org.distorted.examples.predeform;
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.view.Gravity;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
@@ -30,28 +29,30 @@ 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.effect.Effect;
+import org.distorted.library.effect.EffectName;
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.type.Static4D;
+
+import java.util.ArrayList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class PredeformActivity extends Activity
-                               implements View.OnClickListener, AdapterView.OnItemSelectedListener
+public class PredeformActivity extends Activity implements View.OnClickListener, AdapterView.OnItemSelectedListener
   {
-  private static final int COLOR_OFF = 0xffffe81f;
-  private static final int COLOR_ON  = 0xff0000ff;
-  private static final int COLOR_INAC= 0xff999999;
-
   private int mNumCols = 1;
   private int mNumRows = 1;
   private int mNumSlic = 1;
-  private boolean mGridInitialized;
   private NumberPicker mColsPicker, mRowsPicker, mSlicPicker;
-  private boolean[] mShape;
   private int mObjectType;
   private int mBitmapID;
-  private LinearLayout mLay;
+  private EffectName[] mEffectNames;
+  private int mEffectAdd;
+  private LinearLayout mLayout;
+  private int mChildren;
+  private ArrayList<PredeformEffect> mList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -60,13 +61,15 @@ public class PredeformActivity extends Activity
     {
     super.onCreate(savedState);
 
-    setContentView(R.layout.objectpickerlayout);
+    setContentView(R.layout.predeformpickerlayout);
 
-    mLay = findViewById(R.id.objectpicker_buttongrid);
+    mLayout = findViewById(R.id.predeformLayout);
+    mChildren = 0;
+    mList = new ArrayList<>();
 
-    mColsPicker = findViewById(R.id.objectpicker_cols);
-    mRowsPicker = findViewById(R.id.objectpicker_rows);
-    mSlicPicker = findViewById(R.id.objectpicker_slices);
+    mColsPicker = findViewById(R.id.predeformCols);
+    mRowsPicker = findViewById(R.id.predeformRows);
+    mSlicPicker = findViewById(R.id.predeformSlices);
 
     mColsPicker.setMaxValue(40);
     mColsPicker.setMinValue( 1);
@@ -80,7 +83,7 @@ public class PredeformActivity extends Activity
          @Override
          public void onValueChange(NumberPicker picker, int oldVal, int newVal)
            {
-           setGrid();
+           mNumCols = newVal;
            }
          });
 
@@ -89,7 +92,7 @@ public class PredeformActivity extends Activity
          @Override
          public void onValueChange(NumberPicker picker, int oldVal, int newVal)
            {
-           setGrid();
+           mNumRows = newVal;
            }
          });
 
@@ -98,14 +101,13 @@ public class PredeformActivity extends Activity
          @Override
          public void onValueChange(NumberPicker picker, int oldVal, int newVal)
            {
-           mNumSlic = mSlicPicker.getValue();
+           mNumSlic = newVal;
            }
          });
 
     mObjectType = 0;
-    mGridInitialized = false;
 
-    Spinner typeSpinner  = findViewById(R.id.objectpicker_spinnerType);
+    Spinner typeSpinner  = findViewById(R.id.predeformSpinnerType);
     typeSpinner.setOnItemSelectedListener(this);
 
     String[] objectType = new String[PredeformMeshList.LENGTH];
@@ -119,7 +121,7 @@ public class PredeformActivity extends Activity
     adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
     typeSpinner.setAdapter(adapterType);
 
-    Spinner bitmapSpinner  = findViewById(R.id.objectpicker_spinnerBitmap);
+    Spinner bitmapSpinner  = findViewById(R.id.predeformSpinnerBitmap);
     bitmapSpinner.setOnItemSelectedListener(this);
 
     String[] objectBitmap = new String[] { "Texture: Grid", "Texture: Girl", "Texture: Dog", "Texture: Cat",
@@ -128,93 +130,157 @@ public class PredeformActivity extends Activity
     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);
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    Spinner effectSpinner  = findViewById(R.id.predeformSpinnerEffect);
+    effectSpinner.setOnItemSelectedListener(this);
 
-  private void setGrid()
-    {
-    mGridInitialized = true;
+    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, createEffectNames());
+    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    effectSpinner.setAdapter(adapterEffect);
 
-    mNumCols = mColsPicker.getValue();
-    mNumRows = mRowsPicker.getValue();
+    Button add = findViewById(R.id.predeformAddEffect);
+    add.setOnClickListener(this);
+    Button rem = findViewById(R.id.predeformRemoveEffect);
+    rem.setOnClickListener(this);
+    Button cre = findViewById(R.id.predeformCreate);
+    cre.setOnClickListener(this);
+    }
 
-    int width = mLay.getWidth();
-    int height= mLay.getHeight();
-    int w = mNumCols>0 ? (int)( 0.9f*width / mNumCols) : 0;
-    int h = mNumRows>0 ? (int)( 0.9f*height/ mNumRows) : 0;
-    int size= Math.min(w, h);
-    int pad = size<20 ? 1 : size/20;
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    mLay.removeAllViews();
+    private String[] createEffectNames()
+      {
+      EffectType type;
+      EffectName[] names = EffectName.values();
 
-    mShape = new boolean[mNumRows*mNumCols];
+      int numEffects=0;
 
-    TableRow.LayoutParams p = new TableRow.LayoutParams();
+      for (EffectName effectName : names)
+        {
+        type = effectName.getType();
+        if (type == EffectType.VERTEX) numEffects++;
+        }
 
-    p.rightMargin  = pad;
-    p.leftMargin   = pad;
-    p.topMargin    = pad;
-    p.bottomMargin = pad;
-    p.height       = size;
-    p.width        = size;
+      mEffectNames = new EffectName[numEffects];
+      String[] effectStrings = new String[numEffects];
 
-    for (int rows=0; rows<mNumRows; rows++)
-      {
-      TableRow tr = new TableRow(this);
-      tr.setGravity(Gravity.CENTER);
+      numEffects=0;
 
-      for(int cols=0; cols<mNumCols; cols++)
+      for (EffectName name : names)
         {
-        Button b = new Button(this);
-        b.setOnClickListener(this);
-        b.setId(rows*mNumCols+cols);
-        b.setLayoutParams(p);
-        b.setBackgroundColor(mObjectType==0 ? COLOR_ON:COLOR_INAC);
-        tr.addView(b, p);
-        mShape[rows*mNumCols+cols] = true;
+        type = name.getType();
+
+        if (type == EffectType.VERTEX)
+          {
+          mEffectNames[numEffects] = name;
+          effectStrings[numEffects] = name.name();
+
+          numEffects++;
+          }
         }
 
-      mLay.addView(tr);
+      return effectStrings;
       }
-    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void onClick(View view)
+  public void newEffect()
     {
-    if( mObjectType==0 )  // cubes
+    PredeformEffect eff = new PredeformEffect(mEffectNames[mEffectAdd], this );
+    mList.add(eff);
+
+    mChildren++;
+
+    View view = eff.createView(mChildren);
+    mLayout.addView(view);
+
+    int dimension = eff.getDimension();
+    Button butt;
+
+    switch(dimension)
+      {
+      case 1: butt = view.findViewById(R.id.button1dRemove);
+              butt.setOnClickListener(this);
+              break;
+      case 2: butt = view.findViewById(R.id.button2dRemove);
+              butt.setOnClickListener(this);
+              break;
+      case 3: butt = view.findViewById(R.id.button3dRemove);
+              butt.setOnClickListener(this);
+              break;
+      case 4: butt = view.findViewById(R.id.button4dRemove);
+              butt.setOnClickListener(this);
+              break;
+      case 5: butt = view.findViewById(R.id.button5dRemove);
+              butt.setOnClickListener(this);
+              break;
+      }
+
+    if( mEffectNames[mEffectAdd].getCenterDimension() > 0 )
+      {
+      View center = eff.createCenter(mChildren);
+      mLayout.addView(center);
+      }
+
+    if( mEffectNames[mEffectAdd].getRegionDimension() > 0 )
       {
-      Button tmp = (Button)view;
-      int id = tmp.getId();
-      mShape[id] = !mShape[id];
-      tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
+      View region = eff.createRegion(mChildren);
+      mLayout.addView(region);
       }
+
+    Effect effect = eff.createEffect();
+
+    PredeformEffectList.addNew(effect);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void uncheckAll()
+  public void removeAll()
     {
-    TableRow tr;
-    Button butt;
+    mChildren = 0;
+    mList.clear();
+    mLayout.removeAllViews();
+    PredeformEffectList.removeAll();
+    }
 
-    for (int row=0; row<mNumRows; row++)
-      {
-      tr = (TableRow)mLay.getChildAt(row);
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-      for(int col=0; col<mNumCols; col++)
+  public void remove(View v)
+    {
+    for(PredeformEffect effect: mList)
+      {
+      if( effect.thisView(v) )
         {
-        butt = (Button)tr.getVirtualChildAt(col);
-        butt.setBackgroundColor(mObjectType==0 ? COLOR_ON : COLOR_INAC);
-        mShape[row*mNumCols+col] = true;
+        View view;
+
+        view = effect.getEffect();
+        if( view!=null ) mLayout.removeView(view);
+        view = effect.getCenter();
+        if( view!=null ) mLayout.removeView(view);
+        view = effect.getRegion();
+        if( view!=null ) mLayout.removeView(view);
+
+        PredeformEffectList.remove(effect.getId());
+
+        int index = mList.indexOf(effect);
+        int capac = mList.size();
+
+        for(int i=index+1; i<capac; i++)
+          {
+          mList.get(i).setBackground(i-1);
+          }
+
+        mList.remove(effect);
+        mChildren--;
+
+        break;
         }
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void Create(View v)
+  public void create()
     {
     Intent mainInt = new Intent( getApplicationContext(), PredeformActivity2.class);
     Bundle b = new Bundle();
@@ -225,53 +291,81 @@ public class PredeformActivity extends Activity
     b.putInt("slices", PredeformMeshList.getSlic(mObjectType, mNumCols, mNumRows, mNumSlic) );
     b.putInt("bitmap", mBitmapID);
 
-    b.putString("string", PredeformMeshList.getString(mObjectType, mNumCols, mNumRows, mShape));
-
     mainInt.putExtras(b);
     startActivity(mainInt);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
 
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+  int getScreenWidth()
     {
-    switch(parent.getId())
-      {
-      case R.id.objectpicker_spinnerType  : if( mObjectType!=pos )
-                                              {
-                                              mObjectType = pos;
-                                              uncheckAll();
-
-                                              int dim = PredeformMeshList.getDimension(mObjectType);
-
-                                              mRowsPicker.setEnabled(dim>=1);
-                                              mColsPicker.setEnabled(dim>=2);
-                                              mSlicPicker.setEnabled(dim>=3);
-                                              }
-                                            break;
-      case R.id.objectpicker_spinnerBitmap: switch(pos)
-                                              {
-                                              case 0: mBitmapID = -1            ; break;
-                                              case 1: mBitmapID = R.raw.face    ; break;
-                                              case 2: mBitmapID = R.raw.dog     ; break;
-                                              case 3: mBitmapID = R.raw.cat     ; break;
-                                              case 4: mBitmapID = R.raw.grid    ; break;
-                                              case 5: mBitmapID = R.raw.bean    ; break;
-                                              case 6: mBitmapID = R.raw.monalisa; break;
-                                              case 7: mBitmapID = R.raw.world   ; break;
-                                              }
-                                            break;
-      }
+    return 1080;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// only important for matrix effects, ignore here
 
-  public void onNothingSelected(AdapterView<?> parent)
+  float getScaleFactor()
     {
+    return 1.0f;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Overrides
+// TODO
+
+  void setCenter(float x, float y, float z)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  void setRegion(Static4D region)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getWidth()
+    {
+    return mNumCols;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getHeight()
+    {
+    return mNumRows;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getDepth()
+    {
+    return mNumSlic;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onClick(View v)
+    {
+    switch (v.getId())
+      {
+      case R.id.predeformAddEffect   : newEffect(); break;
+      case R.id.predeformRemoveEffect: removeAll(); break;
+      case R.id.predeformCreate      : create();    break;
+      case R.id.button1dRemove       :
+      case R.id.button2dRemove       :
+      case R.id.button3dRemove       :
+      case R.id.button4dRemove       :
+      case R.id.button5dRemove       : remove(v)  ; break;
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @Override
@@ -282,7 +376,47 @@ public class PredeformActivity extends Activity
     mColsPicker.setValue(mNumCols);
     mRowsPicker.setValue(mNumRows);
     mSlicPicker.setValue(mNumSlic);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    if( !mGridInitialized ) setGrid();
+  @Override
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    switch(parent.getId())
+      {
+      case R.id.predeformSpinnerType  : if( mObjectType!=pos )
+                                          {
+                                          mObjectType = pos;
+
+                                          int dim = PredeformMeshList.getDimension(mObjectType);
+
+                                          mRowsPicker.setEnabled(dim>=1);
+                                          mColsPicker.setEnabled(dim>=2);
+                                          mSlicPicker.setEnabled(dim>=3);
+                                          }
+                                        break;
+      case R.id.predeformSpinnerBitmap: switch(pos)
+                                          {
+                                          case 0: mBitmapID = -1            ; break;
+                                          case 1: mBitmapID = R.raw.face    ; break;
+                                          case 2: mBitmapID = R.raw.dog     ; break;
+                                          case 3: mBitmapID = R.raw.cat     ; break;
+                                          case 4: mBitmapID = R.raw.grid    ; break;
+                                          case 5: mBitmapID = R.raw.bean    ; break;
+                                          case 6: mBitmapID = R.raw.monalisa; break;
+                                          case 7: mBitmapID = R.raw.world   ; break;
+                                          }
+                                        break;
+      case R.id.predeformSpinnerEffect: mEffectAdd = pos;
+                                        break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onNothingSelected(AdapterView<?> parent)
+    {
     }
   }
diff --git a/src/main/java/org/distorted/examples/predeform/PredeformActivity2.java b/src/main/java/org/distorted/examples/predeform/PredeformActivity2.java
index 4bfdc7f..6816cd1 100644
--- a/src/main/java/org/distorted/examples/predeform/PredeformActivity2.java
+++ b/src/main/java/org/distorted/examples/predeform/PredeformActivity2.java
@@ -92,16 +92,18 @@ public class PredeformActivity2 extends Activity implements SeekBar.OnSeekBarCha
 
       Bundle b = getIntent().getExtras();
 
-      String str     = b.getString("string");
       int objectType = b.getInt("type");
       int bitmapID   = b.getInt("bitmap");
       mNumCols       = b.getInt("cols");
       mNumRows       = b.getInt("rows");
       mNumSlic       = b.getInt("slices");
 
+      StringBuilder strBuilder = new StringBuilder();
+      for(int i=0; i<mNumCols*mNumRows; i++) strBuilder.append('1');
+
       int maxsize = mNumCols > mNumRows ? (Math.max(mNumCols, mNumSlic)) : (Math.max(mNumRows, mNumSlic));
       createBitmap(maxsize,bitmapID);
-      mMesh =  PredeformMeshList.createMesh(objectType, mNumCols, mNumRows, mNumSlic, bitmapID, str);
+      mMesh =  PredeformMeshList.createMesh(objectType, mNumCols, mNumRows, mNumSlic, bitmapID, strBuilder.toString() );
 
       mTexture = new DistortedTexture();
 
diff --git a/src/main/java/org/distorted/examples/predeform/PredeformEffect.java b/src/main/java/org/distorted/examples/predeform/PredeformEffect.java
new file mode 100644
index 0000000..09e956e
--- /dev/null
+++ b/src/main/java/org/distorted/examples/predeform/PredeformEffect.java
@@ -0,0 +1,871 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.predeform;
+
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.distorted.examples.R;
+import org.distorted.library.effect.Effect;
+import org.distorted.library.effect.EffectName;
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.effect.FragmentEffectAlpha;
+import org.distorted.library.effect.FragmentEffectBrightness;
+import org.distorted.library.effect.FragmentEffectChroma;
+import org.distorted.library.effect.FragmentEffectContrast;
+import org.distorted.library.effect.FragmentEffectSaturation;
+import org.distorted.library.effect.MatrixEffectMove;
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.effect.MatrixEffectRotate;
+import org.distorted.library.effect.MatrixEffectScale;
+import org.distorted.library.effect.MatrixEffectShear;
+import org.distorted.library.effect.PostprocessEffectBlur;
+import org.distorted.library.effect.PostprocessEffectGlow;
+import org.distorted.library.effect.VertexEffectDeform;
+import org.distorted.library.effect.VertexEffectDistort;
+import org.distorted.library.effect.VertexEffectMove;
+import org.distorted.library.effect.VertexEffectPinch;
+import org.distorted.library.effect.VertexEffectQuaternion;
+import org.distorted.library.effect.VertexEffectRotate;
+import org.distorted.library.effect.VertexEffectScale;
+import org.distorted.library.effect.VertexEffectShear;
+import org.distorted.library.effect.VertexEffectSink;
+import org.distorted.library.effect.VertexEffectSwirl;
+import org.distorted.library.effect.VertexEffectWave;
+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 PredeformEffect implements SeekBar.OnSeekBarChangeListener
+  {
+  private static final int BACKGROUND_ODD = 0xff555555;
+  private static final int BACKGROUND_EVEN= 0xff333333;
+
+  private WeakReference<PredeformActivity> mAct;
+
+  private EffectName mName;
+  private int[] mInter;
+  private int[] mInterRegion;
+  private int[] mInterCenter;
+  private int[] mSeekID;
+  private int[] mSeekRegionID;
+  private int[] mSeekCenterID;
+  private int mDimension, mRegionDimension;
+  private TextView mText,mTextRegion,mTextCenter;
+
+  private Dynamic1D mDyn1;
+  private Dynamic2D mDyn2;
+  private Dynamic3D mDyn3;
+  private Dynamic4D mDyn4;
+  private Dynamic5D mDyn5;
+  private Static1D  mSta1;
+  private Static2D  mSta2;
+  private Static3D  mSta3;
+  private Static4D  mSta4;
+  private Static5D  mSta5;
+  private Dynamic4D mRegion4Dyn;
+  private Static4D  mRegion4Sta;
+  private Dynamic3D mRegion3Dyn;
+  private Static3D  mRegion3Sta;
+  private Dynamic3D mCenterDyn;
+  private Static3D  mCenterSta;
+
+  private View mButton, mEffect, mCenter, mRegion;
+  private long mId;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  Effect createEffect()
+    {
+    Effect effect=null;
+
+    switch(mName)
+      {
+      case ROTATE           : effect = new MatrixEffectRotate      (mDyn1, mDyn3, mCenterDyn); break;
+      case QUATERNION       : effect = new MatrixEffectQuaternion  (mDyn4, mCenterDyn)       ; break;
+      case MOVE             : effect = new MatrixEffectMove        (mDyn3)                   ; break;
+      case SCALE            : effect = new MatrixEffectScale       (mDyn3)                   ; break;
+      case SHEAR            : effect = new MatrixEffectShear       (mDyn3, mCenterDyn)       ; break;
+
+      case DISTORT          : effect = new VertexEffectDistort     (mDyn3, mCenterDyn, mRegion4Dyn)              ; break;
+      case DEFORM           : effect = new VertexEffectDeform      (mDyn3, mDyn1, mCenterDyn, mRegion4Dyn)       ; break;
+      case SINK             : effect = new VertexEffectSink        (mDyn1, mCenterDyn, mRegion4Dyn)              ; break;
+      case PINCH            : effect = new VertexEffectPinch       (mDyn3, mCenterDyn, mRegion4Dyn)              ; break;
+      case SWIRL            : effect = new VertexEffectSwirl       (mDyn1, mCenterDyn, mRegion4Dyn)              ; break;
+      case WAVE             : effect = new VertexEffectWave        (mDyn5, mCenterDyn, mRegion4Dyn)              ; break;
+      case VERTEX_MOVE      : effect = new VertexEffectMove        (mDyn3)                                       ; break;
+      case VERTEX_QUATERNION: effect = new VertexEffectQuaternion  (mDyn4, mCenterDyn)                           ; break;
+      case VERTEX_ROTATE    : effect = new VertexEffectRotate      (mDyn1, mDyn3, mCenterDyn)                    ; break;
+      case VERTEX_SCALE     : effect = new VertexEffectScale       (mDyn3)                                       ; break;
+      case VERTEX_SHEAR     : effect = new VertexEffectShear       (mDyn3, mCenterDyn)                           ; break;
+
+      case ALPHA            : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_ALPHA     : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case CHROMA           : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_CHROMA    : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, true ); break;
+      case BRIGHTNESS       : effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_BRIGHTNESS: effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case SATURATION       : effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_SATURATION: effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case CONTRAST         : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_CONTRAST  : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+
+      case BLUR             : effect = new PostprocessEffectBlur   (mDyn2      ); break;
+      case GLOW             : effect = new PostprocessEffectGlow   (mDyn2,mDyn4); break;
+      }
+
+    mId = effect.getID();
+
+    return effect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void fillStatics()
+    {
+    switch(mName)
+      {
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // MATRIX (and VERTEX-pseudo-matrix)
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case VERTEX_ROTATE    :
+      case ROTATE           : float an = (mInter[0]-50)*180/50.0f;
+                              float rx = (mInter[1]-50)/ 50.0f;
+                              float ry = (mInter[2]-50)/ 50.0f;
+                              float rz = (mInter[3]-50)/ 50.0f;
+                              mSta1.set(an);
+                              mSta3.set(rx,ry,rz);
+                              break;
+      case VERTEX_QUATERNION:
+      case QUATERNION       : float qx = (mInter[0]-50)/ 50.0f;
+                              float qy = (mInter[1]-50)/ 50.0f;
+                              float qz = (mInter[2]-50)/ 50.0f;
+                              float qw = (mInter[3]-50)*3.1415f/50;
+                              float cosA = (float)Math.cos(qw/2);
+                              float len = (float)Math.sqrt(qx*qx+qy*qy+qz*qz);
+                              float sinAnorm = (float)Math.sin(qw/2)/len;
+                              mSta4.set(sinAnorm*qx,sinAnorm*qy,sinAnorm*qz, cosA);
+                              break;
+      case MOVE             : float scr= mAct.get().getScreenWidth();
+                              float sw = scr/200.0f;
+                              float sh = scr/200.0f;
+                              float xm = (mInter[0]-50)*sw;
+                              float ym = (mInter[1]-50)*sh;
+                              float zm = (mInter[2]-50)*(sw+sh)/2;
+                              mSta3.set(xm,ym,zm);
+                              break;
+      case VERTEX_SCALE     :
+      case SCALE            : float xs = (mInter[0]>50 ? 0.04f : 0.013f)*(mInter[0]-50) + 1;
+                              float ys = (mInter[1]>50 ? 0.04f : 0.013f)*(mInter[1]-50) + 1;
+                              float zs = (mInter[2]>50 ? 0.04f : 0.013f)*(mInter[2]-50) + 1;
+                              mSta3.set(xs,ys,zs);
+                              break;
+      case VERTEX_SHEAR     :
+      case SHEAR            : float xsh = (mInter[0]-50)/25.0f;
+                              float ysh = (mInter[1]-50)/25.0f;
+                              float zsh = (mInter[2]-50)/25.0f;
+                              mSta3.set(xsh,ysh,zsh);
+                              break;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // VERTEX
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case VERTEX_MOVE      :
+      case DISTORT          : 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 DEFORM           : float le = mAct.get().getWidth()/50.0f;
+                              float xe = (mInter[0]-50)*le;
+                              float ye = (mInter[1]-50)*le;
+                              float ze = (mInter[2]-50)*le;
+                              mSta3.set(xe,ye,ze);
+                              float re = (mInter[3])*le;
+                              mSta1.set(re);
+                              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.0f;
+                              float w2 = (mInter[3]-50)*180 / 50.0f;
+                              float v2 = (mInter[4]-50)*180 / 50.0f;
+                              mSta5.set(x2,y2,z2,w2,v2);
+                              break;
+      case SWIRL            : mSta1.set( 3.6f*(mInter[0]-50) );
+                              break;
+      case SINK             : mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
+                              break;
+      case PINCH            : float dp = mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f;
+                              float ap = (mInter[1]-50)*180 / 50.0f;
+                              float bp = (mInter[2]-50)*180 / 50.0f;
+                              mSta3.set(dp,ap,bp);
+                              break;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // FRAGMENT
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case ALPHA            :
+      case SMOOTH_ALPHA     : mSta1.set(mInter[0]/100.0f);
+                              break;
+      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;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // POSTPROCESS
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case BLUR             : mSta2.set(mInter[0]/2.0f,
+                                        mInter[1]/2.0f);
+                              break;
+      case GLOW             : mSta2.set(mInter[0]/2.0f,
+                                        mInter[1]/2.0f);
+                              mSta4.set(mInter[2]/100.0f,
+                                        mInter[3]/100.0f,
+                                        mInter[4]/100.0f,
+                                        mInter[5]/100.0f );
+                              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultInter()
+    {
+    switch(mDimension)
+      {
+      case 6: mInter[5] = 50;
+      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;
+      }
+
+    if( mName==EffectName.ROTATE         ||
+        mName==EffectName.VERTEX_ROTATE   )
+      {
+      mInter[1]= 100;
+      }
+    if( mName==EffectName.QUATERNION     ||
+        mName==EffectName.VERTEX_QUATERNION )
+      {
+      mInter[0]= 100;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setText()
+    {
+    String text = mName.name();
+
+    if( mSta1 !=null )
+      {
+      float f1 = ((int)(mSta1.get0()*100))/100.0f;
+      text += " "+f1;
+      }
+
+    if( mSta2 !=null )
+      {
+      float f1 = ((int)(mSta2.get0()*100))/100.0f;
+      float f2 = ((int)(mSta2.get1()*100))/100.0f;
+      text += " ("+f1+","+f2+")";
+      }
+
+    if( mSta3 !=null )
+      {
+      float f1 = ((int)(mSta3.get0()*100))/100.0f;
+      float f2 = ((int)(mSta3.get1()*100))/100.0f;
+      float f3 = ((int)(mSta3.get2()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+")";
+      }
+
+    if( mSta4 !=null )
+      {
+      float f1 = ((int)(mSta4.get0()*100))/100.0f;
+      float f2 = ((int)(mSta4.get1()*100))/100.0f;
+      float f3 = ((int)(mSta4.get2()*100))/100.0f;
+      float f4 = ((int)(mSta4.get3()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+","+f4+")";
+      }
+
+    if( mSta5 !=null )
+      {
+      float f1 = ((int)(mSta5.get0()*100))/100.0f;
+      float f2 = ((int)(mSta5.get1()*100))/100.0f;
+      float f3 = ((int)(mSta5.get2()*100))/100.0f;
+      float f4 = ((int)(mSta5.get3()*100))/100.0f;
+      float f5 = ((int)(mSta5.get4()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+","+f4+","+f5+")";
+      }
+
+    mText.setText(text);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillCenterStatics()
+    {
+    PredeformActivity act = mAct.get();
+
+    float x = (mInterCenter[0]*0.012f - 0.6f)*act.getWidth();
+    float y = (mInterCenter[1]*0.012f - 0.6f)*act.getHeight();
+    float z = (mInterCenter[2]*0.012f - 0.6f)*act.getDepth();
+
+    if( mName.getType() == EffectType.MATRIX )
+      {
+      float factor = act.getScaleFactor();
+      x *= factor;
+      y *= factor;
+      z *= factor;
+      }
+
+    mCenterSta.set(x,y,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultCenterInter()
+    {
+    mInterCenter[0] = 50;
+    mInterCenter[1] = 50;
+    mInterCenter[2] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setCenterText()
+    {
+    float f0 = ((int)(mCenterSta.get0()*100))/100.0f;
+    float f1 = ((int)(mCenterSta.get1()*100))/100.0f;
+    float f2 = ((int)(mCenterSta.get2()*100))/100.0f;
+
+    mTextCenter.setText("center ("+f0+","+f1+","+f2+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillRegionStatics()
+    {
+    PredeformActivity act = mAct.get();
+
+    float factorX = act.getWidth() / 100.0f;
+    float factorY = act.getHeight()/ 100.0f;
+ // float factorZ = act.getDepth() / 100.0f;
+
+    int deduct = (mName.getType() == EffectType.VERTEX ? 50:0);
+
+    float x = (mInterRegion[0]-deduct)*factorX;
+    float y = (mInterRegion[1]-deduct)*factorY;
+    float z = (mInterRegion[2]-deduct)*factorX;   // ??
+    float r =  mInterRegion[3]        *(factorX+factorY)/2;
+
+    mRegion4Sta.set(x,y,z,r);
+    mRegion3Sta.set(x,y,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultRegionInter()
+    {
+    mInterRegion[0] = 50;
+    mInterRegion[1] = 50;
+    mInterRegion[2] = 50;
+    mInterRegion[3] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setRegionText()
+    {
+    if( mRegionDimension==4 )
+      {
+      float f0 = ((int)(mRegion4Sta.get0()*100))/100.0f;
+      float f1 = ((int)(mRegion4Sta.get1()*100))/100.0f;
+      float f2 = ((int)(mRegion4Sta.get2()*100))/100.0f;
+      float f3 = ((int)(mRegion4Sta.get3()*100))/100.0f;
+
+      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + "," + f3 + ")");
+      }
+
+    if( mRegionDimension==3 )
+      {
+      float f0 = ((int)(mRegion4Sta.get0()*100))/100.0f;
+      float f1 = ((int)(mRegion4Sta.get1()*100))/100.0f;
+      float f2 = ((int)(mRegion4Sta.get2()*100))/100.0f;
+
+      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + ")");
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setCenter(PredeformActivity act)
+    {
+    float factor = ( mName.getType() == EffectType.MATRIX ? act.getScaleFactor() : 1.0f);
+    act.setCenter( mCenterSta.get0()/factor, mCenterSta.get1()/factor, mCenterSta.get2()/factor );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setBackground(int pos)
+    {
+    int color = (pos%2==1 ? BACKGROUND_ODD:BACKGROUND_EVEN);
+
+    if( mEffect!=null ) mEffect.setBackgroundColor(color);
+    if( mCenter!=null ) mCenter.setBackgroundColor(color);
+    if( mRegion!=null ) mRegion.setBackgroundColor(color);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  PredeformEffect(EffectName name, PredeformActivity act)
+    {
+    mAct = new WeakReference<>(act);
+    mName = name;
+
+    mDyn1 = null;
+    mDyn2 = null;
+    mDyn3 = null;
+    mDyn4 = null;
+    mDyn5 = null;
+    mSta1 = null;
+    mSta2 = null;
+    mSta3 = null;
+    mSta4 = null;
+    mSta5 = null;
+
+    mDimension = mName.getEffectDimension();
+    mRegionDimension = mName.getRegionDimension();
+
+    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 : if( mName == EffectName.QUATERNION || mName == EffectName.VERTEX_QUATERNION )
+                 {
+                 mDyn4 = new Dynamic4D();
+                 mSta4 = new Static4D(0,0,0,0);
+                 mDyn4.add(mSta4);
+                 }
+               else
+                 {
+                 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 : if( mName == EffectName.WAVE )
+                 {
+                 mDyn5 = new Dynamic5D();
+                 mSta5 = new Static5D(0, 0, 0, 0, 0);
+                 mDyn5.add(mSta5);
+                 }
+               else
+                 {
+                 mDyn4 = new Dynamic4D();
+                 mSta4 = new Static4D(0,0,0,0);
+                 mDyn4.add(mSta4);
+                 mDyn1 = new Dynamic1D();
+                 mSta1 = new Static1D(0);
+                 mDyn1.add(mSta1);
+                 }
+               break;
+      case 6 : mDyn2 = new Dynamic2D();
+               mSta2 = new Static2D(0,0);
+               mDyn2.add(mSta2);
+               mDyn4 = new Dynamic4D();
+               mSta4 = new Static4D(0,0,0,0);
+               mDyn4.add(mSta4);
+               break;
+      default: throw new RuntimeException("unsupported effect");
+      }
+
+    mInter = new int[mDimension];
+    mSeekID= new int[mDimension];
+
+    mInterRegion = new int[4];
+    mSeekRegionID= new int[4];
+    mRegion4Dyn  = new Dynamic4D();
+    mRegion4Sta  = new Static4D(0,0,0,0);
+    mRegion4Dyn.add(mRegion4Sta);
+    mRegion3Dyn  = new Dynamic3D();
+    mRegion3Sta  = new Static3D(0,0,0);
+    mRegion3Dyn.add(mRegion3Sta);
+
+    mInterCenter = new int[3];
+    mSeekCenterID= new int[3];
+    mCenterDyn   = new Dynamic3D();
+    mCenterSta   = new Static3D(0,0,0);
+    mCenterDyn.add(mCenterSta);
+
+    mButton = null;
+    mEffect = null;
+    mCenter = null;
+    mRegion = null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createView(int num)
+    {
+    SeekBar[] seek = new SeekBar[mDimension];
+
+    PredeformActivity act = mAct.get();
+
+    switch(mDimension)
+      {
+      case 1 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect1d, null);
+               mText      = mEffect.findViewById(R.id.effect1dText);
+               seek[0]    = 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      = mEffect.findViewById(R.id.effect2dText);
+               seek[0]    = mEffect.findViewById(R.id.effect2dbar1);
+               seek[1]    = 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      = mEffect.findViewById(R.id.effect3dText);
+               seek[0]    = mEffect.findViewById(R.id.effect3dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect3dbar2);
+               seek[2]    = 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      = mEffect.findViewById(R.id.effect4dText);
+               seek[0]    = mEffect.findViewById(R.id.effect4dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect4dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect4dbar3);
+               seek[3]    = 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      = mEffect.findViewById(R.id.effect5dText);
+               seek[0]    = mEffect.findViewById(R.id.effect5dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect5dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect5dbar3);
+               seek[3]    = mEffect.findViewById(R.id.effect5dbar4);
+               seek[4]    = 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;
+      case 6 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect6d, null);
+               mText      = mEffect.findViewById(R.id.effect6dText);
+               seek[0]    = mEffect.findViewById(R.id.effect6dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect6dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect6dbar3);
+               seek[3]    = mEffect.findViewById(R.id.effect6dbar4);
+               seek[4]    = mEffect.findViewById(R.id.effect6dbar5);
+               seek[5]    = mEffect.findViewById(R.id.effect6dbar6);
+               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();
+               mSeekID[5] = seek[5].getId();
+               mButton    = mEffect.findViewById(R.id.button6dRemove);
+               break;
+      default: android.util.Log.e("GenericEffect", "dimension "+mDimension+" not supported!");
+               return null;
+      }
+
+    mEffect.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
+
+    setDefaultInter();
+
+    for(int i=0; i<mDimension; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInter[i] );
+      }
+
+    return mEffect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createRegion(int num)
+    {
+    PredeformActivity act = mAct.get();
+
+    if( mRegionDimension== 4 )
+      {
+      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion4, null);
+      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
+
+      SeekBar[] seek = new SeekBar[4];
+
+      seek[0] = mRegion.findViewById(R.id.effectRegion4BarX);
+      seek[1] = mRegion.findViewById(R.id.effectRegion4BarY);
+      seek[2] = mRegion.findViewById(R.id.effectRegion4BarZ);
+      seek[3] = mRegion.findViewById(R.id.effectRegion4BarR);
+
+      mSeekRegionID[0] = seek[0].getId();
+      mSeekRegionID[1] = seek[1].getId();
+      mSeekRegionID[2] = seek[2].getId();
+      mSeekRegionID[3] = seek[3].getId();
+
+      mTextRegion = mRegion.findViewById(R.id.effectRegion4Text);
+
+      setDefaultRegionInter();
+
+      for (int i = 0; i < 4; i++)
+        {
+        seek[i].setOnSeekBarChangeListener(this);
+        seek[i].setProgress(mInterRegion[i]);
+        }
+
+      act.setRegion(mRegion4Sta);
+      }
+    else
+      {
+      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion3, null);
+      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
+
+      SeekBar[] seek = new SeekBar[3];
+
+      seek[0] = mRegion.findViewById(R.id.effectRegion3BarRX);
+      seek[1] = mRegion.findViewById(R.id.effectRegion3BarRY);
+      seek[2] = mRegion.findViewById(R.id.effectRegion3BarRZ);
+
+      mSeekRegionID[0] = seek[0].getId();
+      mSeekRegionID[1] = seek[1].getId();
+      mSeekRegionID[2] = seek[2].getId();
+
+      mTextRegion = mRegion.findViewById(R.id.effectRegion3Text);
+
+      setDefaultRegionInter();
+
+      for (int i = 0; i < 3; i++)
+        {
+        seek[i].setOnSeekBarChangeListener(this);
+        seek[i].setProgress(mInterRegion[i]);
+        }
+
+      act.setRegion( new Static4D( mRegion3Sta.get0(), mRegion3Sta.get1(), mRegion3Sta.get2(), 1.0f) );
+      }
+
+    return mRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createCenter(int num)
+    {
+    PredeformActivity act = mAct.get();
+
+    mCenter = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
+    mCenter.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
+
+    SeekBar[] seek = new SeekBar[3];
+
+    seek[0] = mCenter.findViewById(R.id.effectCenterBarX );
+    seek[1] = mCenter.findViewById(R.id.effectCenterBarY );
+    seek[2] = mCenter.findViewById(R.id.effectCenterBarZ );
+
+    mSeekCenterID[0] = seek[0].getId();
+    mSeekCenterID[1] = seek[1].getId();
+    mSeekCenterID[2] = seek[2].getId();
+
+    mTextCenter = mCenter.findViewById(R.id.effectCenterText);
+
+    setDefaultCenterInter();
+
+    for(int i=0; i<3; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInterCenter[i] );
+      }
+
+    setCenter(act);
+
+    return mCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    boolean vectorChanged=false;
+    boolean regionChanged=false;
+    boolean centerChanged=false;
+
+    for(int dim=0; dim<6; dim++)
+      {
+      if ( mDimension>dim && bar.getId()==mSeekID[dim] )
+        {
+        mInter[dim] = progress;
+        vectorChanged = true;
+        }
+      }
+
+    if( vectorChanged )
+      {
+      fillStatics();
+      setText();
+      }
+
+    for(int reg=0; reg<4; reg++)
+      {
+      if( mRegionDimension>reg && bar.getId() == mSeekRegionID[reg] )
+        {
+        mInterRegion[reg] = progress;
+        regionChanged = true;
+        }
+      }
+
+    if( regionChanged )
+      {
+      fillRegionStatics();
+      setRegionText();
+      }
+
+    for(int cen=0; cen<3; cen++)
+      {
+      if( bar.getId() == mSeekCenterID[cen] )
+        {
+        mInterCenter[cen] = progress;
+        centerChanged = true;
+        }
+      }
+
+    if( centerChanged )
+      {
+      fillCenterStatics();
+      setCenterText();
+      }
+
+    if( fromUser )
+      {
+      PredeformActivity act = mAct.get();
+      setCenter(act);
+      act.setRegion(mRegion4Sta);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean thisView(View v)
+    {
+    return v==mButton;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getId()
+    {
+    return mId;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getDimension()
+    {
+    return mDimension;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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/predeform/PredeformEffectList.java b/src/main/java/org/distorted/examples/predeform/PredeformEffectList.java
new file mode 100644
index 0000000..f4565bf
--- /dev/null
+++ b/src/main/java/org/distorted/examples/predeform/PredeformEffectList.java
@@ -0,0 +1,71 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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.predeform;
+
+import org.distorted.library.effect.Effect;
+
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class PredeformEffectList
+  {
+  private static ArrayList<Effect> mList = new ArrayList<>();
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void addNew(Effect effect)
+    {
+    mList.add(effect);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void removeAll()
+    {
+    mList.clear();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void remove(long id)
+    {
+    int size = mList.size();
+    Effect effect;
+
+    for (int i=0; i<size; i++)
+      {
+      effect = mList.get(i);
+
+      if (effect.getID() == id )
+        {
+        mList.remove(i);
+        break;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static ArrayList<Effect> getEffectList()
+    {
+    return mList;
+    }
+  }
diff --git a/src/main/res/layout/predeformpickerlayout.xml b/src/main/res/layout/predeformpickerlayout.xml
new file mode 100644
index 0000000..ddc0777
--- /dev/null
+++ b/src/main/res/layout/predeformpickerlayout.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+     android:orientation="vertical"
+     android:layout_width="match_parent"
+     android:layout_height="match_parent">
+
+     <LinearLayout
+           android:orientation="horizontal"
+           android:layout_width="match_parent"
+           android:layout_height="wrap_content">
+
+           <Spinner
+                android:layout_width="0dp"
+                android:layout_height="50dp"
+                android:layout_weight="0.5"
+                android:id="@+id/predeformSpinnerType"/>
+
+           <Spinner
+                android:layout_width="0dp"
+                android:layout_height="50dp"
+                android:layout_weight="0.5"
+                android:id="@+id/predeformSpinnerBitmap"/>
+     </LinearLayout>
+
+     <LinearLayout
+           android:orientation="horizontal"
+           android:layout_width="match_parent"
+           android:layout_height="wrap_content">
+
+           <NumberPicker
+               android:id="@+id/predeformRows"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:orientation="vertical"
+               android:descendantFocusability="blocksDescendants"
+           />
+
+           <NumberPicker
+               android:id="@+id/predeformCols"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:orientation="vertical"
+               android:descendantFocusability="blocksDescendants"
+           />
+
+           <NumberPicker
+               android:id="@+id/predeformSlices"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:orientation="vertical"
+               android:descendantFocusability="blocksDescendants"
+           />
+
+           <Button
+               android:id="@+id/predeformCreate"
+               android:text="@string/Create"
+               android:layout_width="match_parent"
+               android:layout_height="fill_parent"
+           />
+       </LinearLayout>
+
+       <View
+           android:layout_height="4dip"
+           android:background="#777777"
+           android:layout_width="match_parent"
+           />
+
+       <LinearLayout
+           android:layout_width="match_parent"
+           android:layout_height="50dp"
+           android:layout_gravity="center"
+           android:orientation="horizontal">
+
+           <Button
+               android:id="@+id/predeformAddEffect"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_weight="1"
+               android:text="@string/Add"/>
+
+           <TextView
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:text="@string/New"
+               android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+           <Spinner
+               android:id="@+id/predeformSpinnerEffect"
+               android:layout_width="0dp"
+               android:layout_height="wrap_content"
+               android:layout_weight="4"/>
+
+           <Button
+               android:id="@+id/predeformRemoveEffect"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_weight="1"
+               android:text="@string/reset"
+               />
+
+       </LinearLayout>
+
+       <View
+           android:layout_height="4dip"
+           android:background="#777777"
+           android:layout_width="match_parent"
+           />
+
+       <ScrollView
+           android:id="@+id/predeformScrollView"
+           android:layout_width="match_parent"
+           android:layout_height="0dp"
+           android:layout_weight="0.80"
+           >
+
+           <LinearLayout
+               android:id="@+id/predeformLayout"
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content"
+               android:orientation="vertical" >
+           </LinearLayout>
+
+       </ScrollView>
+
+
+</LinearLayout>
\ No newline at end of file
