commit 644c217ac42fd6ce3442b2399df216c23873f97e
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue May 9 15:00:46 2023 +0200

    Progress with support for touch-control of pieces which we rotate along an axs which is normal to the currently touched face (think Coin Tetrahedron!)

diff --git a/src/main/java/org/distorted/objectlib/main/ObjectControl.java b/src/main/java/org/distorted/objectlib/main/ObjectControl.java
index b398e16c..4651fec4 100644
--- a/src/main/java/org/distorted/objectlib/main/ObjectControl.java
+++ b/src/main/java/org/distorted/objectlib/main/ObjectControl.java
@@ -74,6 +74,9 @@ public class ObjectControl
     private int mMeshState, mIconMode;
     private boolean mRotateOnCreation;
     private final Static4D mQuat,mTemp;
+    private boolean mRotAxisIsNormal;
+    private final float[] mNormalTouchPoint;
+    private float mNormalRotAngle;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -254,22 +257,75 @@ public class ObjectControl
 
     private void continueRotation(float x, float y)
       {
-      float dx = x-mStartRotX;
-      float dy = y-mStartRotY;
-      float alpha = dx*mAxis[0] + dy*mAxis[1];
-      float x2 = dx - alpha*mAxis[0];
-      float y2 = dy - alpha*mAxis[1];
+      if( !mRotAxisIsNormal )
+        {
+        float dx = x-mStartRotX;
+        float dy = y-mStartRotY;
+        float alpha = dx*mAxis[0] + dy*mAxis[1];
+        float x2 = dx - alpha*mAxis[0];
+        float y2 = dy - alpha*mAxis[1];
 
-      float len = (float)Math.sqrt(x2*x2 + y2*y2);
+        float len = (float)Math.sqrt(x2*x2 + y2*y2);
 
-      // we have the length of 1D vector 'angle', now the direction:
-      float tmp = mAxis[1]==0 ? -mAxis[0]*y2 : mAxis[1]*x2;
+        // we have the length of 1D vector 'angle', now the direction:
+        float tmp = mAxis[1]==0 ? -mAxis[0]*y2 : mAxis[1]*x2;
 
-      float angle = (tmp>0 ? 1:-1)*len*mRotationFactor;
-      mCurrentAngle = SWIPING_SENSITIVITY*angle;
-      mPreRender.getObject().continueRotation(mCurrentAngle);
+        float angle = (tmp>0 ? 1:-1)*len*mRotationFactor;
+        mCurrentAngle = SWIPING_SENSITIVITY*angle;
+        mPreRender.getObject().continueRotation(mCurrentAngle);
 
-      addSpeedProbe(x2,y2);
+        addSpeedProbe(x2,y2);
+        }
+      else
+        {
+        float rotAngle = computeNormalRotAngle(mNormalTouchPoint[0]-x,mNormalTouchPoint[1]-y);
+        mCurrentAngle = rotAngle - mNormalRotAngle;
+        mPreRender.getObject().continueRotation(mCurrentAngle);
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void correctRotationAxis()
+      {
+      if( mAxis[0]==0 && mAxis[1]==0 )
+        {
+        mRotAxisIsNormal = true;
+
+        // 0. get face center in object space
+        // 1. multiply it by mQuat
+        // 2. multiply if by mObjectRatio. Let the result be (x,y,z)
+        // 3. let D be CAMERA_POINT.Z
+        // 4. then compute the point on the screen (X,Y) the face center maps to:
+        //    (X,Y) = (Ax,Ay) where A = D/(D-z)
+
+        float[] faceCenter = mTouchControl.getTouchedPuzzleCenter();
+        float D = CAMERA_POINT.get2();
+        float[] rotated = new float[4];
+        QuatHelper.rotateVectorByQuat(rotated, faceCenter[0],faceCenter[1],faceCenter[2],faceCenter[3],mQuat);
+        TwistyObject object = getObject();
+        float ratio = object.getObjectRatio();
+
+        float x = rotated[0] * ratio;
+        float y = rotated[1] * ratio;
+        float z = rotated[2] * ratio;
+
+        float A = D / (D-z);
+        mNormalTouchPoint[0] = A*x;
+        mNormalTouchPoint[1] = A*y;
+        }
+      else
+        {
+        mRotAxisIsNormal = false;
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// in degrees
+
+    private float computeNormalRotAngle(float vx, float vy)
+      {
+      return 0;  // TODO
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -290,7 +346,9 @@ public class ObjectControl
       mCurrentRow  = mBuffer[1];
 
       mTouchControl.getCastedRotAxis(mAxis,mQuat,mCurrentAxis);
+      correctRotationAxis();
       mRotationFactor = mTouchControl.returnRotationFactor(numLayers,mCurrentRow);
+      if( mRotAxisIsNormal ) mNormalRotAngle = computeNormalRotAngle(mNormalTouchPoint[0]-x,mNormalTouchPoint[1]-y);
 
       if( object.beginNewRotation( mCurrentAxis, mCurrentRow ) )
         {
@@ -515,6 +573,8 @@ public class ObjectControl
       mLastMode    = -1;
       mRotateOnCreation = false;
 
+      mNormalTouchPoint = new float[2];
+
       mLastX = new float[NUM_SPEED_PROBES];
       mLastY = new float[NUM_SPEED_PROBES];
       mLastT = new long[NUM_SPEED_PROBES];
diff --git a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControl.java b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControl.java
index b50022a8..e009b446 100644
--- a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControl.java
+++ b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControl.java
@@ -133,5 +133,6 @@ public abstract class TouchControl
   public abstract void getCastedRotAxis(float[] output, Static4D quat, int rotIndex);
   public abstract int getTouchedCubitFace();
   public abstract int getTouchedCubit();
+  public abstract float[] getTouchedPuzzleCenter();
   public abstract float returnRotationFactor(int[] numLayers, int row);
   }
diff --git a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlBall.java b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlBall.java
index 366ee6fc..1ae6c2b2 100644
--- a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlBall.java
+++ b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlBall.java
@@ -233,6 +233,13 @@ public class TouchControlBall extends TouchControl
     return 0;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[] getTouchedPuzzleCenter()
+    {
+    return null;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getTouchedCubit()
diff --git a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeChanging.java b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeChanging.java
index b831dcd4..9532af48 100644
--- a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeChanging.java
+++ b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeChanging.java
@@ -590,9 +590,9 @@ public class TouchControlShapeChanging extends TouchControl
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public void prepareAgain()
+  public float[] getTouchedPuzzleCenter()
     {
-    mPreparationDone = false;
+    return null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeConstant.java b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeConstant.java
index c6492423..3ae5f34d 100644
--- a/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeConstant.java
+++ b/src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeConstant.java
@@ -408,6 +408,15 @@ public abstract class TouchControlShapeConstant extends TouchControl
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[] getTouchedPuzzleCenter()
+    {
+    Static3D faceAxis = mFaceAxis[mLastTouchedFace];
+    float d = mDistanceCenterFace3D[mLastTouchedFace];
+    return new float[] { d*faceAxis.get0(), d*faceAxis.get1(), d*faceAxis.get2(), 1 };
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   public int getTouchedCubitFace()
