commit 863084211b16e896fd332c1691d7b0eed8619fdb
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sat May 13 00:19:45 2023 +0200

    Split bandagedObject into abstract part and BandagedCuboid.

diff --git a/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java b/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
index fb2e99ba..3c60738d 100644
--- a/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
+++ b/src/main/java/org/distorted/bandaged/BandagedCreatorRenderer.java
@@ -97,7 +97,7 @@ public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, Distorte
      mScreen = new DistortedScreen();
      mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
      mScale = new Static3D(1,1,1);
-     mObject = new BandagedObject(mScreen);
+     mObject = new BandagedObjectCuboid(mScreen);
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/bandaged/BandagedObject.java b/src/main/java/org/distorted/bandaged/BandagedObject.java
index ad82f7a8..2d69de47 100644
--- a/src/main/java/org/distorted/bandaged/BandagedObject.java
+++ b/src/main/java/org/distorted/bandaged/BandagedObject.java
@@ -9,48 +9,56 @@
 
 package org.distorted.bandaged;
 
-import static org.distorted.objectlib.main.TwistyObject.COLOR_BLUE;
-import static org.distorted.objectlib.main.TwistyObject.COLOR_GREEN;
-import static org.distorted.objectlib.main.TwistyObject.COLOR_ORANGE;
-import static org.distorted.objectlib.main.TwistyObject.COLOR_RED;
-import static org.distorted.objectlib.main.TwistyObject.COLOR_WHITE;
-import static org.distorted.objectlib.main.TwistyObject.COLOR_YELLOW;
-
 import org.distorted.library.main.DistortedNode;
 import org.distorted.library.main.DistortedScreen;
 import org.distorted.library.mesh.MeshBase;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
-import org.distorted.objectlib.helpers.FactoryBandagedCubit;
-import org.distorted.objectlib.main.InitData;
 import org.distorted.objectlib.main.TwistyObject;
-import org.distorted.objectlib.objects.TwistyBandagedCuboid;
-import org.distorted.objectlib.shape.ShapeHexahedron;
-import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-public class BandagedObject
+public abstract class BandagedObject
 {
     private final DistortedScreen mScreen;
-    private final float[] mPos;
-    private final int[] mSize;
-    private final float mDist2D;
+    private final float[][] mFaceAxis;
+    final int[] mSize;
+    final float mDist2D;
 
-    private BandagedCubit[] mCubits;
-    private int mMax;
-    private int mNumCubits;
+    BandagedCubit[] mCubits;
+    int mMax;
+    int mNumCubits;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    BandagedObject(DistortedScreen screen)
      {
      mScreen = screen;
-     mPos = new float[3];
      mSize = new int[3];
      mDist2D = getDist2D();
+
+     Static3D[] axis = getFaceAxis();
+     int numAxis = axis.length;
+     mFaceAxis = new float[numAxis][];
+     for(int i=0; i<numAxis; i++) mFaceAxis[i] = new float[] { axis[i].get0(), axis[i].get1(), axis[1].get2() };
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   abstract float getDist2D();
+   abstract float[] getDist3D();
+   abstract int[] getColors();
+   abstract Static3D[] getFaceAxis();
+   abstract void createCubits(Static4D quatT, Static4D quatA, Static3D scale);
+   abstract boolean isAdjacent(float[] pos1, float[] pos2);
+   abstract int computeProjectionAngle();
+   abstract boolean tryChangeObject(int x, int y, int z);
+   abstract void stretchPoint(int face, float[] output);
+   abstract int whichCubitTouched(int face, float pointX, float pointY);
+   abstract boolean isInsideFace(int face, float[] p);
+   abstract TwistyObject createObject(int mode, float scale );
+   abstract MeshBase createMesh(float[] pos, boolean round);
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    int[] getSize()
@@ -58,6 +66,35 @@ public class BandagedObject
      return mSize;
      }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   int computeMapsIndex(float[] faceAx)
+      {
+      int min=-1, numAxis = mFaceAxis.length;
+      float error = Float.MAX_VALUE;
+      float x = faceAx[0];
+      float y = faceAx[1];
+      float z = faceAx[2];
+
+      for(int i=0; i<numAxis; i++)
+        {
+        float[] ax = mFaceAxis[i];
+        float dx = x-ax[0];
+        float dy = y-ax[1];
+        float dz = z-ax[2];
+
+        float diff = dx*dx + dy*dy + dz*dz;
+
+        if( error>diff )
+          {
+          error = diff;
+          min = i;
+          }
+        }
+
+      return min;
+      }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    void resetObject(float scale)
@@ -77,33 +114,6 @@ public class BandagedObject
        }
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private boolean isAdjacent(float[] pos1, float[] pos2)
-     {
-     int len1 = pos1.length/3;
-     int len2 = pos2.length/3;
-
-     for(int i=0; i<len1; i++)
-       for(int j=0; j<len2; j++)
-         {
-         float d0 = pos1[3*i  ] - pos2[3*j  ];
-         float d1 = pos1[3*i+1] - pos2[3*j+1];
-         float d2 = pos1[3*i+2] - pos2[3*j+2];
-
-         if( d0*d0 + d1*d1 + d2*d2 == 1 ) return true;
-         }
-
-     return false;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private int computeNumCubits(int x, int y, int z)
-     {
-     return ( x<=1 || y<=1 || z<=1 ) ? x*y*z : x*y*z-(x-2)*(y-2)*(z-2);
-     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    float getMaxSize()
@@ -132,15 +142,6 @@ public class BandagedObject
      return pos;
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   TwistyObject createObject(int mode, float size)
-     {
-     float[][] pos = getCubitPositions();
-     InitData data = new InitData( mSize,pos);
-     return new TwistyBandagedCuboid( TwistyObject.MESH_NICE, mode, ShapeHexahedron.DEFAULT_ROT, new Static3D(0,0,0), size, data, null );
-     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    void tryConnectingCubits(int index1, int index2, float scale)
@@ -188,42 +189,6 @@ public class BandagedObject
        }
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   boolean tryChangeObject(int x, int y, int z)
-     {
-     if( mSize[0]!=x || mSize[1]!=y || mSize[2]!=z )
-       {
-       mSize[0] = x;
-       mSize[1] = y;
-       mSize[2] = z;
-       mMax = x>y ? Math.max(x,z) : Math.max(y,z);
-       mNumCubits = computeNumCubits(x,y,z);
-       return true;
-       }
-
-     return false;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   int computeProjectionAngle()
-     {
-     float quot1 = mSize[2]/ (float)mSize[0];
-     float quot2 = mSize[2]/ (float)mSize[1];
-     float quot3 = mSize[0]/ (float)mSize[2];
-     float quot4 = mSize[0]/ (float)mSize[1];
-
-     float quot5 = Math.max(quot1,quot2);
-     float quot6 = Math.max(quot3,quot4);
-     float quot7 = Math.max(quot5,quot6);
-
-          if( quot7<=1.0f ) return 120;
-     else if( quot7<=1.5f ) return 90;
-     else if( quot7<=2.0f ) return 60;
-     else                   return 30;
-     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
    void scaleCubits(float scale)
@@ -245,110 +210,6 @@ public class BandagedObject
         }
      }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   float[] getDist3D()
-     {
-     float max = getMaxSize();
-
-     float x = 0.5f*(mSize[0]/max);
-     float y = 0.5f*(mSize[1]/max);
-     float z = 0.5f*(mSize[2]/max);
-
-     return new float[] {x,x,y,y,z,z};
-     }
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   float getDist2D()
-     {
-     return 0.5f;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// (x,y,z) ==
-//
-// ( 1,0,0) --> 0
-// (-1,0,0) --> 1
-// (0, 1,0) --> 2
-// (0,-1,0) --> 3
-// (0,0, 1) --> 4
-// (0,0,-1) --> 5
-
-   int computeMapsIndex(float[] faceAxis)
-      {
-      float x = faceAxis[0];
-      float y = faceAxis[1];
-      float z = faceAxis[2];
-
-      int ix = x>0 ? (int)(x+0.5f) : (int)(x-0.5f);
-      int iy = y>0 ? (int)(y+0.5f) : (int)(y-0.5f);
-      int iz = z>0 ? (int)(z+0.5f) : (int)(z-0.5f);
-
-      if( ix== 1 ) return 0;
-      if( ix==-1 ) return 1;
-      if( iy== 1 ) return 2;
-      if( iy==-1 ) return 3;
-      if( iz== 1 ) return 4;
-      if( iz==-1 ) return 5;
-
-      return 0;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   MeshBase createMesh(float[] pos, boolean round)
-      {
-      FactoryBandagedCubit factory = FactoryBandagedCubit.getInstance();
-      int[] size = getSize();
-      return factory.createMesh(pos,size[0],size[1],size[2],false,round);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   int[] getColors()
-     {
-     return new int[]
-         {
-           COLOR_YELLOW, COLOR_WHITE,
-           COLOR_BLUE  , COLOR_GREEN,
-           COLOR_RED   , COLOR_ORANGE
-         };
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void createCubits(Static4D quatT, Static4D quatA, Static3D scale)
-     {
-     mCubits = new BandagedCubit[mNumCubits];
-     int c=0;
-     int sx = mSize[0];
-     int sy = mSize[1];
-     int sz = mSize[2];
-
-     float begX = 0.5f*(1-sx);
-     float begY = 0.5f*(1-sy);
-     float begZ = 0.5f*(1-sz);
-
-     for(int x=0; x<sx; x++)
-       for(int y=0; y<sy; y++)
-          for(int z=0; z<sz; z++)
-            if( x==0 || x==sx-1 || y==0 || y==sy-1 || z==0 || z==sz-1 )
-              {
-              float[] pos = new float[] { begX+x,begY+y,begZ+z };
-              mCubits[c] = new BandagedCubit(this,pos,quatT,quatA,scale,false);
-              c++;
-              }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Static3D[] getFaceAxis()
-    {
-    return TouchControlHexahedron.FACE_AXIS;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   void touchCubit(int index)
@@ -362,75 +223,4 @@ public class BandagedObject
     {
     if( index>=0 && index<mNumCubits && mCubits[index]!=null ) mCubits[index].setUnmarked();
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void stretchPoint(int face, float[] output)
-    {
-    float max = getMaxSize();
-
-    switch(face/2)
-      {
-      case 0: output[0] *= (max/mSize[2]); output[1] *= (max/mSize[1]); break;
-      case 1: output[0] *= (max/mSize[0]); output[1] *= (max/mSize[2]); break;
-      case 2: output[0] *= (max/mSize[0]); output[1] *= (max/mSize[1]); break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int whichCubitTouched(int face, float pointX, float pointY)
-    {
-    float x = mSize[0];
-    float y = mSize[1];
-    float z = mSize[2];
-
-    switch(face)
-      {
-      case 0: mPos[0] = (x-1)/2;
-              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
-              mPos[2] = (int)(-z*pointX-z/2)+(z-1)/2;
-              break;
-      case 1: mPos[0] =-(x-1)/2;
-              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
-              mPos[2] = (int)( z*pointX+z/2)-(z-1)/2;
-              break;
-      case 2: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
-              mPos[1] = (y-1)/2;
-              mPos[2] = (int)(-z*pointY-z/2)+(z-1)/2;
-              break;
-      case 3: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
-              mPos[1] =-(y-1)/2;
-              mPos[2] = (int)( z*pointY+z/2)-(z-1)/2;
-              break;
-      case 4: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
-              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
-              mPos[2] = (z-1)/2;
-              break;
-      case 5: mPos[0] = (int)(-x*pointX-x/2)+(x-1)/2;
-              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
-              mPos[2] =-(z-1)/2;
-              break;
-      }
-
-    for(int c=0; c<mNumCubits; c++)
-      if( mCubits[c].isAttached() )
-        {
-        float[] pos = mCubits[c].getPosition();
-        int len = pos.length/3;
-
-        for(int p=0; p<len; p++)
-          if( pos[3*p]==mPos[0] && pos[3*p+1]==mPos[1] && pos[3*p+2]==mPos[2] ) return c;
-        }
-
-    android.util.Log.e("D", "whichCubitTouched: IMPOSSIBLE!!");
-    return -1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean isInsideFace(int face, float[] p)
-    {
-    return ( p[0]<=mDist2D && p[0]>=-mDist2D && p[1]<=mDist2D && p[1]>=-mDist2D );
-    }
 }
diff --git a/src/main/java/org/distorted/bandaged/BandagedObjectCuboid.java b/src/main/java/org/distorted/bandaged/BandagedObjectCuboid.java
new file mode 100644
index 00000000..3fa36168
--- /dev/null
+++ b/src/main/java/org/distorted/bandaged/BandagedObjectCuboid.java
@@ -0,0 +1,240 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.bandaged;
+
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.objectlib.helpers.FactoryBandagedCubit;
+import org.distorted.objectlib.main.InitData;
+import org.distorted.objectlib.main.TwistyObject;
+import org.distorted.objectlib.objects.TwistyBandagedCuboid;
+import org.distorted.objectlib.shape.ShapeHexahedron;
+import org.distorted.objectlib.touchcontrol.TouchControlHexahedron;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class BandagedObjectCuboid extends BandagedObject
+{
+   private final float[] mPos;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   BandagedObjectCuboid(DistortedScreen screen)
+     {
+     super(screen);
+     mPos = new float[3];
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   boolean isAdjacent(float[] pos1, float[] pos2)
+     {
+     int len1 = pos1.length/3;
+     int len2 = pos2.length/3;
+
+     for(int i=0; i<len1; i++)
+       for(int j=0; j<len2; j++)
+         {
+         float d0 = pos1[3*i  ] - pos2[3*j  ];
+         float d1 = pos1[3*i+1] - pos2[3*j+1];
+         float d2 = pos1[3*i+2] - pos2[3*j+2];
+
+         if( d0*d0 + d1*d1 + d2*d2 == 1 ) return true;
+         }
+
+     return false;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   TwistyObject createObject(int mode, float size)
+     {
+     float[][] pos = getCubitPositions();
+     InitData data = new InitData( mSize,pos);
+     return new TwistyBandagedCuboid( TwistyObject.MESH_NICE, mode, ShapeHexahedron.DEFAULT_ROT, new Static3D(0,0,0), size, data, null );
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   boolean tryChangeObject(int x, int y, int z)
+     {
+     if( mSize[0]!=x || mSize[1]!=y || mSize[2]!=z )
+       {
+       mSize[0] = x;
+       mSize[1] = y;
+       mSize[2] = z;
+       mMax = x>y ? Math.max(x,z) : Math.max(y,z);
+       mNumCubits = ( x<=1 || y<=1 || z<=1 ) ? x*y*z : x*y*z-(x-2)*(y-2)*(z-2);
+       return true;
+       }
+
+     return false;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   int computeProjectionAngle()
+     {
+     float quot1 = mSize[2]/ (float)mSize[0];
+     float quot2 = mSize[2]/ (float)mSize[1];
+     float quot3 = mSize[0]/ (float)mSize[2];
+     float quot4 = mSize[0]/ (float)mSize[1];
+
+     float quot5 = Math.max(quot1,quot2);
+     float quot6 = Math.max(quot3,quot4);
+     float quot7 = Math.max(quot5,quot6);
+
+          if( quot7<=1.0f ) return 120;
+     else if( quot7<=1.5f ) return 90;
+     else if( quot7<=2.0f ) return 60;
+     else                   return 30;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   float[] getDist3D()
+     {
+     float max = mMax;
+
+     float x = 0.5f*(mSize[0]/max);
+     float y = 0.5f*(mSize[1]/max);
+     float z = 0.5f*(mSize[2]/max);
+
+     return new float[] {x,x,y,y,z,z};
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   float getDist2D()
+     {
+     return 0.5f;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   MeshBase createMesh(float[] pos, boolean round)
+      {
+      FactoryBandagedCubit factory = FactoryBandagedCubit.getInstance();
+      int[] size = getSize();
+      return factory.createMesh(pos,size[0],size[1],size[2],false,round);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   int[] getColors()
+     {
+     return ShapeHexahedron.FACE_COLORS;
+     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+   void createCubits(Static4D quatT, Static4D quatA, Static3D scale)
+     {
+     mCubits = new BandagedCubit[mNumCubits];
+     int c=0;
+     int sx = mSize[0];
+     int sy = mSize[1];
+     int sz = mSize[2];
+
+     float begX = 0.5f*(1-sx);
+     float begY = 0.5f*(1-sy);
+     float begZ = 0.5f*(1-sz);
+
+     for(int x=0; x<sx; x++)
+       for(int y=0; y<sy; y++)
+          for(int z=0; z<sz; z++)
+            if( x==0 || x==sx-1 || y==0 || y==sy-1 || z==0 || z==sz-1 )
+              {
+              float[] pos = new float[] { begX+x,begY+y,begZ+z };
+              mCubits[c] = new BandagedCubit(this,pos,quatT,quatA,scale,false);
+              c++;
+              }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getFaceAxis()
+    {
+    return TouchControlHexahedron.FACE_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void stretchPoint(int face, float[] output)
+    {
+    float max = getMaxSize();
+
+    switch(face/2)
+      {
+      case 0: output[0] *= (max/mSize[2]); output[1] *= (max/mSize[1]); break;
+      case 1: output[0] *= (max/mSize[0]); output[1] *= (max/mSize[2]); break;
+      case 2: output[0] *= (max/mSize[0]); output[1] *= (max/mSize[1]); break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int whichCubitTouched(int face, float pointX, float pointY)
+    {
+    float x = mSize[0];
+    float y = mSize[1];
+    float z = mSize[2];
+
+    switch(face)
+      {
+      case 0: mPos[0] = (x-1)/2;
+              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
+              mPos[2] = (int)(-z*pointX-z/2)+(z-1)/2;
+              break;
+      case 1: mPos[0] =-(x-1)/2;
+              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
+              mPos[2] = (int)( z*pointX+z/2)-(z-1)/2;
+              break;
+      case 2: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
+              mPos[1] = (y-1)/2;
+              mPos[2] = (int)(-z*pointY-z/2)+(z-1)/2;
+              break;
+      case 3: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
+              mPos[1] =-(y-1)/2;
+              mPos[2] = (int)( z*pointY+z/2)-(z-1)/2;
+              break;
+      case 4: mPos[0] = (int)( x*pointX+x/2)-(x-1)/2;
+              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
+              mPos[2] = (z-1)/2;
+              break;
+      case 5: mPos[0] = (int)(-x*pointX-x/2)+(x-1)/2;
+              mPos[1] = (int)( y*pointY+y/2)-(y-1)/2;
+              mPos[2] =-(z-1)/2;
+              break;
+      }
+
+    for(int c=0; c<mNumCubits; c++)
+      if( mCubits[c].isAttached() )
+        {
+        float[] pos = mCubits[c].getPosition();
+        int len = pos.length/3;
+
+        for(int p=0; p<len; p++)
+          if( pos[3*p]==mPos[0] && pos[3*p+1]==mPos[1] && pos[3*p+2]==mPos[2] ) return c;
+        }
+
+    android.util.Log.e("D", "whichCubitTouched: IMPOSSIBLE!!");
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean isInsideFace(int face, float[] p)
+    {
+    return ( p[0]<=mDist2D && p[0]>=-mDist2D && p[1]<=mDist2D && p[1]>=-mDist2D );
+    }
+}
