commit 9694d2d518e0b662d2a03d121c55c7078274e87a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat Apr 30 00:52:06 2022 +0200

    Bandaged: add support for two-fingered rotation in the Creator.

diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorView.java b/src/main/java/org/distorted/bandaged/BandagedCreatorView.java
index 9af5fd46..ca3d6847 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorView.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorView.java
@@ -35,13 +35,16 @@ import org.distorted.library.main.DistortedScreen;
 
 public class BandagedCreatorView extends GLSurfaceView
 {
-    private final static int DIRECTION_SENSITIVITY=  12;
+    private final static float DIRECTION_SENSITIVITY= 12.0f;
+    public static final int INVALID_POINTER_ID = -1;
 
     private BandagedCreatorRenderer mRenderer;
     private BandagedCreatorTouchControl mTouchControl;
     private int mScreenWidth, mScreenHeight, mScreenMin;
     private int mTouchedIndex1, mTouchedIndex2;
-    private int mX, mY;
+    private float mX1, mY1, mX2, mY2, mX, mY;
+    private int mPointer1, mPointer2;
+    private float mRotAngle, mInitDistance;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
@@ -133,6 +136,193 @@ public class BandagedCreatorView extends GLSurfaceView
       mScreenMin   = Math.min(width, height);
       }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private float getAngle(float x1, float y1, float x2, float y2)
+      {
+      return (float) Math.atan2(y1-y2, x1-x2);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void actionDown(MotionEvent event)
+      {
+      mPointer1 = event.getPointerId(0);
+      mX1 = event.getX();
+      mY1 = event.getY();
+      mPointer2 = INVALID_POINTER_ID;
+
+      float x1 = (mX1 -mScreenWidth*0.5f)/mScreenMin;
+      float y1 = (mScreenHeight*0.5f-mY1)/mScreenMin;
+
+      int index = mTouchControl.cubitTouched(x1,y1,mRenderer.getQuatAccu() );
+
+      if( index<0 )
+        {
+        mX = mX1;
+        mY = mY1;
+        }
+      else
+        {
+        mX = -1;
+        mY = -1;
+
+        if( mTouchedIndex1<0 )
+          {
+          mTouchedIndex1 = index;
+          mRenderer.touchCubit(mTouchedIndex1);
+          }
+        else
+          {
+          mTouchedIndex2 = index;
+
+          if( mTouchedIndex1 != index )
+            {
+            mRenderer.touchCubit(mTouchedIndex2);
+            }
+          }
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void actionMove(MotionEvent event)
+      {
+      int index1 = event.findPointerIndex(mPointer1);
+
+      if( index1>=0 )
+        {
+        mX1 = event.getX(index1);
+        mY1 = event.getY(index1);
+        }
+
+      int index2 = event.findPointerIndex(mPointer2);
+
+      if( index2>=0 )
+        {
+        mX2 = event.getX(index2);
+        mY2 = event.getY(index2);
+        }
+
+      if( mPointer1!=INVALID_POINTER_ID && mPointer2!=INVALID_POINTER_ID)
+        {
+        float angleNow = getAngle(mX1,mY1,mX2,mY2);
+        float angleDiff = angleNow-mRotAngle;
+        float sinA = (float)Math.sin(angleDiff);
+        float cosA = (float)Math.cos(angleDiff);
+        mRenderer.setQuatTemp(0,0,sinA,cosA);
+        mRenderer.resetQuats();
+        mRotAngle = angleNow;
+
+/*
+        float distNow  = (float)Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) );
+        float distQuot = mInitDistance<0 ? 1.0f : distNow/ mInitDistance;
+        mInitDistance = distNow;
+        TwistyObject object = mPreRender.getObject();
+        if( object!=null ) object.setObjectRatio(distQuot, mObjectNode.getMinSize() );
+ */
+        }
+      else
+        {
+        float x = event.getX();
+        float y = event.getY();
+
+        if( mX>=0 && mY>= 0 )
+          {
+          float px = mY-y;
+          float py = mX-x;
+          float pz = 0;
+          float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
+
+          if( plen>0 )
+            {
+            px /= plen;
+            py /= plen;
+            pz /= plen;
+
+            float cosA = (float)Math.cos(plen*3.14f/mScreenMin);
+            float sinA = (float)Math.sqrt(1-cosA*cosA);
+
+            mRenderer.setQuatTemp(px*sinA, py*sinA, pz*sinA, cosA);
+            }
+          }
+        if( (mX-x)*(mX-x) + (mY-y)*(mY-y) > mScreenMin*mScreenMin/(DIRECTION_SENSITIVITY*DIRECTION_SENSITIVITY) )
+          {
+          mX = x;
+          mY = y;
+          mRenderer.resetQuats();
+          }
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void actionUp()
+      {
+      mPointer1 = INVALID_POINTER_ID;
+      mPointer2 = INVALID_POINTER_ID;
+
+      if( mTouchedIndex2>=0 )
+        {
+        mRenderer.untouchCubit(mTouchedIndex1);
+        mRenderer.untouchCubit(mTouchedIndex2);
+        mRenderer.setConnecting(mTouchedIndex1,mTouchedIndex2);
+        mTouchedIndex1 = -1;
+        mTouchedIndex2 = -1;
+        }
+
+      mX = -1;
+      mY = -1;
+
+      mRenderer.resetQuats();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void actionPointerDown(MotionEvent event)
+      {
+      int index = event.getActionIndex();
+
+      if( mPointer1==INVALID_POINTER_ID )
+        {
+        mPointer1 = event.getPointerId(index);
+        mX1 = event.getX(index);
+        mY1 = event.getY(index);
+        }
+      else if( mPointer2==INVALID_POINTER_ID )
+        {
+        mPointer2 = event.getPointerId(index);
+        mX2 = event.getX(index);
+        mY2 = event.getY(index);
+        }
+
+      mRotAngle = getAngle(mX1,mY1,mX2,mY2);
+      mInitDistance = -1;
+      mRenderer.resetQuats();
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void actionPointerUp(MotionEvent event)
+      {
+      int index = event.getActionIndex();
+
+      if( index==event.findPointerIndex(mPointer1) )
+        {
+        mPointer1 = INVALID_POINTER_ID;
+        mX = mX2;
+        mY = mY2;
+        }
+      else if( index==event.findPointerIndex(mPointer2) )
+        {
+        mPointer2 = INVALID_POINTER_ID;
+        mX = mX1;
+        mY = mY1;
+        }
+
+      mRenderer.resetQuats();
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
     @Override
@@ -140,86 +330,15 @@ public class BandagedCreatorView extends GLSurfaceView
       {
       if( mRenderer.isBusy() ) return true;
 
-      int action = event.getAction();
-      int x = (int)event.getX();
-      int y = (int)event.getY();
+      int action = event.getActionMasked();
 
       switch(action)
          {
-         case MotionEvent.ACTION_DOWN: float x1 = (x -mScreenWidth*0.5f)/mScreenMin;
-                                       float y1 = (mScreenHeight*0.5f-y)/mScreenMin;
-
-                                       int index = mTouchControl.cubitTouched(x1,y1,mRenderer.getQuatAccu() );
-
-                                       if( index<0 )
-                                         {
-                                         mX = x;
-                                         mY = y;
-                                         }
-                                       else
-                                         {
-                                         mX = -1;
-                                         mY = -1;
-
-                                         if( mTouchedIndex1<0 )
-                                           {
-                                           mTouchedIndex1 = index;
-                                           mRenderer.touchCubit(mTouchedIndex1);
-                                           }
-                                         else
-                                           {
-                                           mTouchedIndex2 = index;
-
-                                           if( mTouchedIndex1 != index )
-                                             {
-                                             mRenderer.touchCubit(mTouchedIndex2);
-                                             }
-                                           }
-                                         }
-
-                                       break;
-
-         case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
-                                         {
-                                         float px = mY-y;
-                                         float py = mX-x;
-                                         float pz = 0;
-                                         float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
-
-                                         if( plen>0 )
-                                           {
-                                           px /= plen;
-                                           py /= plen;
-                                           pz /= plen;
-
-                                           float cosA = (float)Math.cos(plen*3.14f/mScreenMin);
-                                           float sinA = (float)Math.sqrt(1-cosA*cosA);
-
-                                           mRenderer.setQuatTemp(px*sinA, py*sinA, pz*sinA, cosA);
-                                           }
-                                         }
-                                       if( (mX-x)*(mX-x) + (mY-y)*(mY-y) > mScreenMin*mScreenMin/(DIRECTION_SENSITIVITY*DIRECTION_SENSITIVITY) )
-                                         {
-                                         mX = x;
-                                         mY = y;
-                                         mRenderer.resetQuats();
-                                         }
-                                       break;
-
-         case MotionEvent.ACTION_UP  : if( mTouchedIndex2>=0 )
-                                         {
-                                         mRenderer.untouchCubit(mTouchedIndex1);
-                                         mRenderer.untouchCubit(mTouchedIndex2);
-                                         mRenderer.setConnecting(mTouchedIndex1,mTouchedIndex2);
-                                         mTouchedIndex1 = -1;
-                                         mTouchedIndex2 = -1;
-                                         }
-
-                                       mX = -1;
-                                       mY = -1;
-
-        	                             mRenderer.resetQuats();
-                                       break;
+         case MotionEvent.ACTION_DOWN        : actionDown(event)       ; break;
+         case MotionEvent.ACTION_MOVE        : actionMove(event)       ; break;
+         case MotionEvent.ACTION_UP          : actionUp()              ; break;
+         case MotionEvent.ACTION_POINTER_DOWN: actionPointerDown(event); break;
+         case MotionEvent.ACTION_POINTER_UP  : actionPointerUp  (event); break;
          }
 
       return true;
