commit dd65ead37aa6b89464a3c68e139bff34ef17ae25
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Mar 11 11:53:27 2020 +0000

    Big progress with generalizing the Movement classes.

diff --git a/src/main/java/org/distorted/object/RubikCubeMovement.java b/src/main/java/org/distorted/object/RubikCubeMovement.java
index 3524320d..6a6d77ee 100644
--- a/src/main/java/org/distorted/object/RubikCubeMovement.java
+++ b/src/main/java/org/distorted/object/RubikCubeMovement.java
@@ -19,163 +19,77 @@
 
 package org.distorted.object;
 
-import org.distorted.library.type.Static2D;
-import org.distorted.library.type.Static4D;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class RubikCubeMovement extends RubikObjectMovement
 {
-    private final static int LEFT   = 0;  // axisX left
-    private final static int RIGHT  = 1;  // axisX right
-    private final static int BOTTOM = 2;  // axisY left
-    private final static int TOP    = 3;  // axisY right
-    private final static int BACK   = 4;  // axisZ left
-    private final static int FRONT  = 5;  // axisZ right
-
-    private static final int VECTX  = 0;  //
-    private static final int VECTY  = 1;  // don't change this
-    private static final int VECTZ  = 2;  //
-
-    private float[] mPoint, mCamera, mDiff, mTouch;
-    private int mRotationVect, mLastTouchedAxis, mLastTouchedLR;
-    private int mNumAxis, mNumFacesPerAxis;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private boolean isVertical(float x, float y)
-      {
-      return (y>x) ? (y>=-x) : (y< -x);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private int retFaceRotationSign(int axis, int lr)
-      {
-      int face = axis*mNumFacesPerAxis + lr;
-      return (face==BACK || face==RIGHT || face==TOP) ? 1:-1;
-      }
+  RubikCubeMovement()
+    {
+    super(3,2);
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private int retFaceXaxis(int axis)
-      {
-      return axis==0 ? VECTZ : VECTX;
-      }
+  private boolean isVertical(float x, float y)
+    {
+    return (y>x) ? (y>=-x) : (y< -x);
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    private int retFaceYaxis(int axis)
-      {
-      return axis==1 ? VECTZ : VECTY;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    private boolean faceIsVisible(int axis, int lr)
-      {
-      return (lr==0 ? -1:1)*mCamera[axis] > 0.5f;
-      }
+  boolean faceIsVisible(int axis, int lr)
+    {
+    return (2*lr-1)*mCamera[axis] > 0.5f;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
 // cast this touch point onto the surface defined by the 'face' and write the cast coords to 'output'.
 // Center of the 'face' = (0,0), third coord always +- cubeHalfSize.
 
-    private void castTouchPointOntoFace(int axis, int lr, float[] output)
-      {
-      float diff = mPoint[axis]-mCamera[axis];
-      float ratio =  diff!=0.0f ? ( (lr-0.5f)-mCamera[axis])/diff : 0.0f;
+  void castTouchPointOntoFace(int axis, int lr, float[] output)
+    {
+    float diff = mPoint[axis]-mCamera[axis];
+    float ratio= diff!=0.0f ? ((lr-0.5f)-mCamera[axis])/diff : 0.0f;
 
-      output[0] = (mPoint[0]-mCamera[0])*ratio + mCamera[0];
-      output[1] = (mPoint[1]-mCamera[1])*ratio + mCamera[1];
-      output[2] = (mPoint[2]-mCamera[2])*ratio + mCamera[2];
-      }
+    output[0] = (mPoint[0]-mCamera[0])*ratio + mCamera[0];
+    output[1] = (mPoint[1]-mCamera[1])*ratio + mCamera[1];
+    output[2] = (mPoint[2]-mCamera[2])*ratio + mCamera[2];
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    RubikCubeMovement()
+  void fillPossibleRotations(int axis, int[] output)
+    {
+    switch(axis)
       {
-      mPoint = new float[3];
-      mCamera= new float[3];
-      mDiff  = new float[3];
-      mTouch = new float[3];
-
-      mNumAxis = RubikCube.AXIS.length;
-      mNumFacesPerAxis = RubikCube.FACE_COLORS.length / mNumAxis;
+      case 0: output[0]=2; output[1]=1; break; // (Z,Y) when looking at LEFT or RIGHT
+      case 1: output[0]=0; output[1]=2; break; // (X,Z) when looking at BOTTOM or TOP
+      case 2: output[0]=0; output[1]=1; break; // (X,Y) when looking at FRONT or BACK
       }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public boolean faceTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
-      {
-      mPoint[0]  = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO;
-      mPoint[1]  = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO;
-      mPoint[2]  = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO;
-
-      mCamera[0] = rotatedCamera.get0()/RubikObject.OBJECT_SCREEN_RATIO;
-      mCamera[1] = rotatedCamera.get1()/RubikObject.OBJECT_SCREEN_RATIO;
-      mCamera[2] = rotatedCamera.get2()/RubikObject.OBJECT_SCREEN_RATIO;
-
-      for( mLastTouchedAxis=0; mLastTouchedAxis<mNumAxis; mLastTouchedAxis++)
-        {
-        for( mLastTouchedLR=0; mLastTouchedLR<mNumFacesPerAxis; mLastTouchedLR++)
-          {
-          if( faceIsVisible(mLastTouchedAxis, mLastTouchedLR) )
-            {
-            castTouchPointOntoFace(mLastTouchedAxis, mLastTouchedLR, mTouch);
-
-            if( mTouch[0]<=0.5f && mTouch[0]>=-0.5f &&
-                mTouch[1]<=0.5f && mTouch[1]>=-0.5f &&
-                mTouch[2]<=0.5f && mTouch[2]>=-0.5f  )  return true;
-            }
-          }
-        }
-
-      return false;
-      }
+  boolean isInsideFace(float[] p)
+    {
+    return ( p[0]<=0.5f && p[0]>=-0.5f && p[1]<=0.5f && p[1]>=-0.5f && p[2]<=0.5f && p[2]>=-0.5f );
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public Static2D newRotation(Static4D rotatedTouchPoint)
-      {
-      mPoint[0] = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO;
-      mPoint[1] = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO;
-      mPoint[2] = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO;
-
-      castTouchPointOntoFace(mLastTouchedAxis, mLastTouchedLR, mDiff);
-
-      mDiff[0] -= mTouch[0];
-      mDiff[1] -= mTouch[1];
-      mDiff[2] -= mTouch[2];
-
-      int xAxis = retFaceXaxis(mLastTouchedAxis);
-      int yAxis = retFaceYaxis(mLastTouchedAxis);
-      mRotationVect = (isVertical( mDiff[xAxis], mDiff[yAxis]) ? xAxis : yAxis);
-      float offset= mTouch[mRotationVect]+0.5f;
-
-      mTouch[0] = mPoint[0];
-      mTouch[1] = mPoint[1];
-      mTouch[2] = mPoint[2];
-
-      return new Static2D(mRotationVect,offset);
-      }
+  float fillUpRotationVectAndOffset(float[] v, int[] possible)
+    {
+    mRotationVect = isVertical(v[possible[0]],v[possible[1]]) ? possible[0] : possible[1];
+    return mTouch[mRotationVect]+0.5f;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    public float continueRotation(Static4D rotatedTouchPoint)
-      {
-      mDiff[0] = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[0];
-      mDiff[1] = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[1];
-      mDiff[2] = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[2];
-
-      int xAxis= retFaceXaxis(mLastTouchedAxis);
-      int yAxis= retFaceYaxis(mLastTouchedAxis);
-      int sign = retFaceRotationSign(mLastTouchedAxis, mLastTouchedLR);
-      float angle = (mRotationVect==xAxis ? mDiff[yAxis] : -mDiff[xAxis]);
-
-      return sign*angle*0.5f;
-      }
+  float returnAngle(float[] v, int[] possible)
+    {
+    float angle= (mRotationVect==possible[0] ? v[possible[1]] : -v[possible[0]]);
+    if( mLastTouchedAxis==2 ) angle = -angle;
+    return angle;
+    }
 }
diff --git a/src/main/java/org/distorted/object/RubikObjectMovement.java b/src/main/java/org/distorted/object/RubikObjectMovement.java
index 487f2658..2e558a69 100644
--- a/src/main/java/org/distorted/object/RubikObjectMovement.java
+++ b/src/main/java/org/distorted/object/RubikObjectMovement.java
@@ -26,7 +26,102 @@ import org.distorted.library.type.Static4D;
 
 public abstract class RubikObjectMovement
   {
-  public abstract boolean faceTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera);
-  public abstract Static2D newRotation(Static4D rotatedTouchPoint);
-  public abstract float continueRotation(Static4D rotatedTouchPoint);
+  float[] mPoint, mCamera, mTouch;
+  int mRotationVect, mLastTouchedAxis;
+
+  private float[] mDiff;
+  private int mLastTouchedLR;
+  private int mNumAxis, mNumFacesPerAxis;
+  private int[] mPossible;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  abstract boolean faceIsVisible(int axis, int lr);
+  abstract void castTouchPointOntoFace(int axis, int lr, float[] output);
+  abstract boolean isInsideFace(float[] point);
+  abstract void fillPossibleRotations(int axis, int[] output);
+  abstract float fillUpRotationVectAndOffset(float[] vect, int[] possible);
+  abstract float returnAngle(float[] vect, int[] possible);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikObjectMovement(int numAxis, int numFacesPerAxis)
+    {
+    mPoint = new float[3];
+    mCamera= new float[3];
+    mDiff  = new float[3];
+    mTouch = new float[3];
+
+    mNumAxis = numAxis;
+    mNumFacesPerAxis = numFacesPerAxis;
+    mPossible = new int[mNumAxis-1];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean faceTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
+    {
+    mPoint[0]  = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO;
+    mPoint[1]  = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO;
+    mPoint[2]  = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO;
+
+    mCamera[0] = rotatedCamera.get0()/RubikObject.OBJECT_SCREEN_RATIO;
+    mCamera[1] = rotatedCamera.get1()/RubikObject.OBJECT_SCREEN_RATIO;
+    mCamera[2] = rotatedCamera.get2()/RubikObject.OBJECT_SCREEN_RATIO;
+
+    for( mLastTouchedAxis=0; mLastTouchedAxis<mNumAxis; mLastTouchedAxis++)
+      {
+      for( mLastTouchedLR=0; mLastTouchedLR<mNumFacesPerAxis; mLastTouchedLR++)
+        {
+        if( faceIsVisible(mLastTouchedAxis, mLastTouchedLR) )
+          {
+          castTouchPointOntoFace(mLastTouchedAxis, mLastTouchedLR, mTouch);
+
+          if( isInsideFace(mTouch) )
+            {
+            fillPossibleRotations(mLastTouchedAxis, mPossible);
+            return true;
+            }
+          }
+        }
+      }
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Static2D newRotation(Static4D rotatedTouchPoint)
+    {
+    mPoint[0] = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO;
+    mPoint[1] = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO;
+    mPoint[2] = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO;
+
+    castTouchPointOntoFace(mLastTouchedAxis, mLastTouchedLR, mDiff);
+
+    mDiff[0] -= mTouch[0];
+    mDiff[1] -= mTouch[1];
+    mDiff[2] -= mTouch[2];
+
+    float offset = fillUpRotationVectAndOffset(mDiff, mPossible);
+
+    mTouch[0] = mPoint[0];
+    mTouch[1] = mPoint[1];
+    mTouch[2] = mPoint[2];
+
+    return new Static2D(mRotationVect,offset);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float continueRotation(Static4D rotatedTouchPoint)
+    {
+    mDiff[0] = rotatedTouchPoint.get0()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[0];
+    mDiff[1] = rotatedTouchPoint.get1()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[1];
+    mDiff[2] = rotatedTouchPoint.get2()/RubikObject.OBJECT_SCREEN_RATIO - mTouch[2];
+
+    return (mLastTouchedLR-0.5f)*returnAngle(mDiff, mPossible);
+    }
   }
diff --git a/src/main/java/org/distorted/object/RubikPyraminxMovement.java b/src/main/java/org/distorted/object/RubikPyraminxMovement.java
index 42c09ddd..8e182e5a 100644
--- a/src/main/java/org/distorted/object/RubikPyraminxMovement.java
+++ b/src/main/java/org/distorted/object/RubikPyraminxMovement.java
@@ -19,41 +19,89 @@
 
 package org.distorted.object;
 
-import org.distorted.library.type.Static2D;
-import org.distorted.library.type.Static4D;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 class RubikPyraminxMovement extends RubikObjectMovement
 {
+  private static final float SQ2 = (float)Math.sqrt(2);
+  private static final float SQ3 = (float)Math.sqrt(3);
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    RubikPyraminxMovement()
-      {
+  RubikPyraminxMovement()
+    {
+    super(4,1);
+    }
 
-      }
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean faceIsVisible(int axis, int lr)
+    {
+    return mCamera[axis] < -SQ2*SQ3/12;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
+// TODO
+
+  void castTouchPointOntoFace(int axis, int lr, float[] output)
+    {
+    /*
+    float diff = mPoint[axis]-mCamera[axis];
+    float ratio= diff!=0.0f ? ((lr-0.5f)-mCamera[axis])/diff : 0.0f;
+
+    output[0] = (mPoint[0]-mCamera[0])*ratio + mCamera[0];
+    output[1] = (mPoint[1]-mCamera[1])*ratio + mCamera[1];
+    output[2] = (mPoint[2]-mCamera[2])*ratio + mCamera[2];
+    */
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// (         0,        1,       0 )  BOTTOM
+// (         0,  -1.0f/3, 2*SQ2/3 )  BACK
+// (-SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 )  LEFT
+// ( SQ2*SQ3/3,  -1.0f/3,  -SQ2/3 )  RIGHT
 
-    public boolean faceTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
+  void fillPossibleRotations(int axis, int[] output)
+    {
+    switch(axis)
       {
-      return false;
+      case 0: output[0]=3; output[1]=1; output[2]=2; break; // (RIGHT,BACK,LEFT) when looking at BOTTOM
+      case 1: output[0]=2; output[1]=0; output[2]=3; break; // (LEFT,BOTTOM,RIGHT) when looking at BACK
+      case 2: output[0]=3; output[1]=0; output[2]=1; break; // (RIGHT,BOTTOM,BACK) when looking at LEFT
+      case 3: output[0]=1; output[1]=0; output[2]=2; break; // (BACK,BOTTOM,LEFT) when looking at RIGHT
       }
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
 
-    public Static2D newRotation(Static4D rotatedTouchPoint)
-      {
-      return null;
-      }
+  boolean isInsideFace(float[] p)
+    {
+    return false;//( p[0]<=0.5f && p[0]>=-0.5f && p[1]<=0.5f && p[1]>=-0.5f && p[2]<=0.5f && p[2]>=-0.5f );
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
 
-    public float continueRotation(Static4D rotatedTouchPoint)
-      {
-      return 0.0f;
-      }
+  float fillUpRotationVectAndOffset(float[] v, int[] possible)
+    {
+    //mRotationVect = isVertical(v[possible[0]],v[possible[1]]) ? possible[0] : possible[1];
+    //return mTouch[mRotationVect]+0.5f;
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  float returnAngle(float[] v, int[] possible)
+    {
+    /*
+    float angle= (mRotationVect==possible[0] ? v[possible[1]] : -v[possible[0]]);
+    if( mLastTouchedAxis==2 ) angle = -angle;
+    return angle;
+     */
+
+    return 0;
+    }
 }
