commit 4c0a6d974dcf8a26896fa401308160c43976b486
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Mar 15 15:59:40 2021 +0100

    Beginnings of support for bandaged versions of the 3x3 cube.

diff --git a/src/main/java/org/distorted/objects/FactoryCubit.java b/src/main/java/org/distorted/objects/FactoryCubit.java
index 63435290..ffb203a7 100644
--- a/src/main/java/org/distorted/objects/FactoryCubit.java
+++ b/src/main/java/org/distorted/objects/FactoryCubit.java
@@ -209,26 +209,6 @@ class FactoryCubit
       }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Compute (rx,ry) - coords of a point which is the result of rotation by angle 'radians' of the
-// point (px,py) along axis Z. Center of rotation: (cx,cy). Rotation is counterclockwise!
-// Write (rx,ry) to array[index] and array[index+1].
-
-  private void writeVertex( float cx, float cy, float px, float py, float radians, float[] array, int index)
-    {
-    float vx = px-cx;
-    float vy = py-cy;
-
-    float sinA = (float)Math.sin(radians);
-    float cosA = (float)Math.cos(radians);
-
-    float rvx = vx*cosA - vy*sinA;
-    float rvy = vx*sinA + vy*cosA;
-
-    array[index  ] = rvx + cx;
-    array[index+1] = rvy + cy;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createFacesCube(int sizeIndex)
@@ -831,6 +811,50 @@ class FactoryCubit
     return new MeshJoined(meshes);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private float[] createVertices(int A, int B)
+    {
+    float E = 0.5f / Math.max(A,B);
+    return new float[] { -A*E,-B*E, +A*E,-B*E, +A*E,+B*E, -A*E,+B*E };
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCuboid(int[] dimensions)
+    {
+    int X = dimensions[0];
+    int Y = dimensions[1];
+    int Z = dimensions[2];
+
+    float[] verticesXY = createVertices(X,Y);
+    float[] verticesXZ = createVertices(X,Z);
+    float[] verticesYZ = createVertices(Z,Y);
+
+    float defHeight = 0.048f;
+
+    float[] bandsX = computeBands( defHeight/X,65,0.25f,0.5f,5);
+    float[] bandsY = computeBands( defHeight/Y,65,0.25f,0.5f,5);
+    float[] bandsZ = computeBands( defHeight/Z,65,0.25f,0.5f,5);
+
+    MeshBase[] meshes = new MeshPolygon[6];
+
+    meshes[0] = new MeshPolygon(verticesYZ,bandsX,1,2);
+    meshes[0].setEffectAssociation(0,1,0);
+    meshes[1] = meshes[0].copy(true);
+    meshes[1].setEffectAssociation(0,2,0);
+    meshes[2] = new MeshPolygon(verticesXZ,bandsY,1,2);
+    meshes[2].setEffectAssociation(0,4,0);
+    meshes[3] = meshes[2].copy(true);
+    meshes[3].setEffectAssociation(0,8,0);
+    meshes[4] = new MeshPolygon(verticesXY,bandsZ,1,2);
+    meshes[4].setEffectAssociation(0,16,0);
+    meshes[5] = meshes[4].copy(true);
+    meshes[5].setEffectAssociation(0,32,0);
+
+    return new MeshJoined(meshes);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // EFFECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1506,6 +1530,55 @@ class FactoryCubit
     return effect;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  VertexEffect[] createCuboidEffects(int[] dimensions)
+    {
+    float X = dimensions[0];
+    float Y = dimensions[1];
+    float Z = dimensions[2];
+
+    float MAX_XY = Math.max(X,Y);
+    float MAX_XZ = Math.max(X,Z);
+    float MAX_YZ = Math.max(Z,Y);
+
+    Static1D angle = new Static1D(90);
+    Static3D move  = new Static3D( 0.0f, 0.0f, 0.5f);
+    Static3D axisX = new Static3D( 1.0f, 0.0f, 0.0f);
+    Static3D axisY = new Static3D( 0.0f, 1.0f, 0.0f);
+    Static3D center= new Static3D( 0.0f, 0.0f, 0.0f);
+
+    Static3D scale3 = new Static3D(MAX_XY,MAX_XY,+Z);
+    Static3D scale4 = new Static3D(MAX_XY,MAX_XY,-Z);
+    Static3D scale5 = new Static3D(MAX_XZ,+Y,MAX_XZ);
+    Static3D scale6 = new Static3D(MAX_XZ,-Y,MAX_XZ);
+    Static3D scale7 = new Static3D(+X,MAX_YZ,MAX_YZ);
+    Static3D scale8 = new Static3D(-X,MAX_YZ,MAX_YZ);
+
+    VertexEffect[] effect = new VertexEffect[9];
+
+    effect[0] = new VertexEffectMove(move);
+    effect[1] = new VertexEffectRotate(angle, axisX, center);
+    effect[2] = new VertexEffectRotate(angle, axisY, center);
+    effect[3] = new VertexEffectScale(scale3);
+    effect[4] = new VertexEffectScale(scale4);
+    effect[5] = new VertexEffectScale(scale5);
+    effect[6] = new VertexEffectScale(scale6);
+    effect[7] = new VertexEffectScale(scale7);
+    effect[8] = new VertexEffectScale(scale8);
+
+    effect[1].setMeshAssociation(12,-1);  // meshes 2,3
+    effect[2].setMeshAssociation( 3,-1);  // meshes 0,1
+    effect[3].setMeshAssociation(16,-1);  // mesh 4
+    effect[4].setMeshAssociation(32,-1);  // mesh 5
+    effect[5].setMeshAssociation( 8,-1);  // mesh 3
+    effect[6].setMeshAssociation( 4,-1);  // mesh 2
+    effect[7].setMeshAssociation( 1,-1);  // mesh 0
+    effect[8].setMeshAssociation( 2,-1);  // mesh 1
+
+    return effect;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // OBJECTS
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1947,6 +2020,19 @@ class FactoryCubit
     mesh.addEmptyTexComponent();
     mesh.addEmptyTexComponent();
 
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCuboidMesh(int[] dimensions)
+    {
+     MeshBase mesh = createCuboid(dimensions);
+    VertexEffect[] effects = createCuboidEffects(dimensions);
+    for( VertexEffect effect : effects ) mesh.apply(effect);
+
+    mesh.mergeEffComponents();
+
     return mesh;
     }
   }
diff --git a/src/main/java/org/distorted/objects/ObjectList.java b/src/main/java/org/distorted/objects/ObjectList.java
index 91b40829..c31db07c 100644
--- a/src/main/java/org/distorted/objects/ObjectList.java
+++ b/src/main/java/org/distorted/objects/ObjectList.java
@@ -160,6 +160,16 @@ public enum ObjectList
          4,
          30
        ),
+
+  BAN1 (
+         new int[][] {
+                       {3 , 16, R.raw.mega3, R.drawable.ui_small_mega3, R.drawable.ui_medium_mega3, R.drawable.ui_big_mega3, R.drawable.ui_huge_mega3} ,
+                     },
+         TwistyBandagedFused.class,
+         new MovementCube(),
+         5,
+         60
+       ),
   ;
 
   public static final int NUM_OBJECTS = values().length;
@@ -541,18 +551,19 @@ public enum ObjectList
 
     switch(ordinal())
       {
-      case  0: return new TwistyCube      (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  1: return new TwistyPyraminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  2: return new TwistyDiamond   (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  3: return new TwistyDino6     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  4: return new TwistyDino4     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  5: return new TwistyRedi      (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  6: return new TwistyHelicopter(size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  7: return new TwistySkewb     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  8: return new TwistyIvy       (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case  9: return new TwistyRex       (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 10: return new TwistyKilominx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 11: return new TwistyMegaminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  0: return new TwistyCube         (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  1: return new TwistyPyraminx     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  2: return new TwistyDiamond      (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  3: return new TwistyDino6        (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  4: return new TwistyDino4        (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  5: return new TwistyRedi         (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  6: return new TwistyHelicopter   (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  7: return new TwistySkewb        (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  8: return new TwistyIvy          (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case  9: return new TwistyRex          (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 10: return new TwistyKilominx     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 11: return new TwistyMegaminx     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 12: return new TwistyBandagedFused(size, quat, texture, mesh, effects, moves, res, scrWidth);
       }
 
     return null;
diff --git a/src/main/java/org/distorted/objects/TwistyBandagedCube.java b/src/main/java/org/distorted/objects/TwistyBandagedCube.java
new file mode 100644
index 00000000..5b3f2858
--- /dev/null
+++ b/src/main/java/org/distorted/objects/TwistyBandagedCube.java
@@ -0,0 +1,479 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2021 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube 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.                                                           //
+//                                                                                               //
+// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objects;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshSquare;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.main.RubikSurfaceView;
+
+import java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+abstract class TwistyBandagedCube extends TwistyObject
+{
+  // the three rotation axis of a 3x3 Cube. Must be normalized.
+  static final Static3D[] ROT_AXIS = new Static3D[]
+         {
+           new Static3D(1,0,0),
+           new Static3D(0,1,0),
+           new Static3D(0,0,1)
+         };
+
+  private static final int[] FACE_COLORS = new int[]
+         {
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_ORANGE
+         };
+
+  private static final Static4D[] QUATS = new Static4D[]
+         {
+         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
+         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
+         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
+         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
+
+         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
+         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
+         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
+         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
+         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
+         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
+         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
+         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
+         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
+         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
+         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
+         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
+
+         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
+         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
+         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
+         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
+         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
+         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
+         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
+         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
+         };
+
+  // possible aspectRatios of cubit faces in case of a bandaged 3x3:
+  // 1/3, 1/2, 2/3, 1, 3/2, 2, 3. Numerator = (1/3)*3!, (1/2)*3!, ...
+  private static final int[] mAspectNumerator3 = new int[] {2,3,4,6,9,12,18};
+  private static final int[] mAspectRatio3     = new int[] {0,0,0,0,0,0,0};
+
+  static final Static4D[] INIT_QUATS = new Static4D[]
+        {
+        new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),  // NULL
+        new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),  // X
+        new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),  // Y
+        new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),  // Z
+        new Static4D( -0.5f,  +0.5f,  -0.5f,  -0.5f),  // ZX
+        new Static4D( +0.5f,  +0.5f,  +0.5f,  +0.5f),  // YX
+        };
+
+  private static MeshBase[] mMeshes;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  TwistyBandagedCube(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+                     DistortedEffects effects, int[][] moves, ObjectList list, Resources res, int scrWidth)
+    {
+    super(size, size, quat, texture, mesh, effects, moves, list, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  abstract int getNumCubitVariants();
+  abstract int getCubitVariant(int cubit);
+  abstract int getNumCubits();
+  abstract int[] getCubitDimensions(int variant);
+  abstract Static3D getCubitPosition(int cubit);
+  abstract int getQuatIndex(int cubit);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCubitMesh(int cubit, int numLayers)
+    {
+    if( mMeshes==null )
+      {
+      mMeshes = new MeshBase[getNumCubitVariants()];
+      }
+
+    int variant = getCubitVariant(cubit);
+
+    if( mMeshes[variant]==null )
+      {
+      int[] dimensions = getCubitDimensions(variant);
+      mMeshes[variant] = FactoryCubit.getInstance().createCuboidMesh(dimensions);
+      }
+
+    MeshBase mesh = mMeshes[variant].copy(true);
+    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( INIT_QUATS[getQuatIndex(cubit)], new Static3D(0,0,0) );
+    mesh.apply(quat,0xffffffff,0);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getAspectNumerator(int stickerType)
+    {
+    int type=-1;
+    int len = mAspectRatio3.length;
+
+    for(int i=0; i<len; i++)
+      {
+      if( mAspectRatio3[i] != 0 ) type++;
+      if( type==stickerType ) return mAspectNumerator3[i];
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
+    {
+    float R = 0.10f;
+    float S = 0.08f;
+    int numFaces = FACE_COLORS.length;
+    int stickerType = face/numFaces;
+    float ratio = getAspectNumerator(stickerType)/6.0f;
+    float X,Y;
+
+    if( ratio<1.0f )
+      {
+      X = ratio/2;
+      Y = 0.5f;
+      }
+    else
+      {
+      X = 0.5f;
+      Y = 0.5f/ratio;
+      }
+
+    float[] vertices = { -X,-Y, +X,-Y, +X,+Y, -X,+Y};
+
+    FactorySticker factory = FactorySticker.getInstance();
+    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face%numFaces], R);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getCubitPositions(int size)
+    {
+    int numCubits = getNumCubits();
+    Static3D[] tmp = new Static3D[numCubits];
+
+    for(int cubit=0; cubit<numCubits; cubit++)
+      {
+      tmp[cubit] = getCubitPosition(cubit);
+      }
+
+    return tmp;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static4D[] getQuats()
+    {
+    return QUATS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean shouldResetTextureMaps()
+    {
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumFaces()
+    {
+    return FACE_COLORS.length;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getCuts(int numLayers)
+    {
+    float[] cuts = new float[numLayers-1];
+
+    for(int i=0; i<numLayers-1; i++)
+      {
+      cuts[i] = (2-numLayers)*0.5f + i;
+      }
+
+    return cuts;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumStickerTypes(int numLayers)
+    {
+    if( numLayers==3 )
+      {
+      int numVariants = getNumCubitVariants();
+      int len = mAspectNumerator3.length;
+
+      for(int variant=0; variant<numVariants; variant++)
+        {
+        int[] dimensions = getCubitDimensions(variant);
+
+        int ratio0 = 6*dimensions[0]/dimensions[1];
+        int ratio1 = 6*dimensions[0]/dimensions[2];
+        int ratio2 = 6*dimensions[2]/dimensions[1];
+
+        for(int i=0; i<len; i++)
+          {
+          if( mAspectNumerator3[i]==ratio0 ||
+              mAspectNumerator3[i]==ratio1 ||
+              mAspectNumerator3[i]==ratio2  ) mAspectRatio3[i]++;
+          }
+        }
+
+      int result=0;
+
+      for(int i=0; i<len; i++)
+        {
+        if( mAspectRatio3[i]>0 ) result++;
+        }
+
+      return result;
+      }
+
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitFaces()
+    {
+    return FACE_COLORS.length;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float getScreenRatio()
+    {
+    return 0.5f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getFaceColor(int cubit, int cubitface, int numLayers)
+    {
+    int X=0,Y=0,Z=0;
+    int variant = getCubitVariant(cubit);
+    int[] dim = getCubitDimensions(variant);
+    Static3D pos = getCubitPosition(cubit);
+
+    switch( getQuatIndex(cubit) )
+      {
+      case 0: X= dim[0]; Y=dim[1]; Z=dim[2]; break;
+      case 1: X= dim[0]; Y=dim[2]; Z=dim[1]; break;
+      case 2: X= dim[2]; Y=dim[1]; Z=dim[0]; break;
+      case 3: X= dim[1]; Y=dim[0]; Z=dim[2]; break;
+      case 4: X= dim[1]; Y=dim[2]; Z=dim[0]; break;
+      case 5: X= dim[2]; Y=dim[0]; Z=dim[1]; break;
+      }
+
+    float posX = pos.get0();
+    float posY = pos.get1();
+    float posZ = pos.get2();
+    float border = (numLayers-1)*0.5f;
+    int ret = NUM_FACES;
+
+    switch(cubitface)
+      {
+      case 0: ret = posX + X*0.5f > border ? cubitface : NUM_FACES; break;
+      case 1: ret = posX - X*0.5f <-border ? cubitface : NUM_FACES; break;
+      case 2: ret = posY + Y*0.5f > border ? cubitface : NUM_FACES; break;
+      case 3: ret = posY - Y*0.5f <-border ? cubitface : NUM_FACES; break;
+      case 4: ret = posZ + Z*0.5f > border ? cubitface : NUM_FACES; break;
+      case 5: ret = posZ - Z*0.5f <-border ? cubitface : NUM_FACES; break;
+      }
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float returnMultiplier()
+    {
+    return getNumLayers();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances(int numLayers)
+    {
+    float[] chances = new float[numLayers];
+
+    for(int i=0; i<numLayers; i++)
+      {
+      chances[i] = (i+1.0f) / numLayers;
+      }
+
+    return chances;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getBasicAngle()
+    {
+    return 4;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
+    {
+    int numAxis = ROTATION_AXIS.length;
+
+    if( oldRotAxis == START_AXIS )
+      {
+      return rnd.nextInt(numAxis);
+      }
+    else
+      {
+      int newVector = rnd.nextInt(numAxis-1);
+      return (newVector>=oldRotAxis ? newVector+1 : newVector);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
+    {
+    float rowFloat = rnd.nextFloat();
+
+    for(int row=0; row<mRowChances.length; row++)
+      {
+      if( rowFloat<=mRowChances[row] ) return row;
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean isSolved()
+    {
+    int index = CUBITS[0].mQuatIndex;
+
+    for(int i=1; i<NUM_CUBITS; i++)
+      {
+      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
+      }
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
+// then if it were rotated by quaternion 'quat'.
+// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
+// middle squares get interchanged. No visible difference!
+//
+// So: this is true iff the cubit
+// a) is a corner or edge and the quaternions are the same
+// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
+
+  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
+    {
+    if ( cubit.mQuatIndex == quatIndex ) return true;
+
+    int belongsToHowManyFaces = 0;
+    int lastLayer = getNumLayers()-1;
+    float row;
+    final float MAX_ERROR = 0.01f;
+
+    for(int i=0; i<NUM_AXIS; i++)
+      {
+      row = cubit.mRotationRow[i];
+      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
+          (row-lastLayer<MAX_ERROR && row-lastLayer>-MAX_ERROR)  ) belongsToHowManyFaces++;
+      }
+
+    switch(belongsToHowManyFaces)
+      {
+      case 0 : return true ;  // 'inside' cubit that does not lie on any face
+      case 1 :                // cubit that lies inside one of the faces
+               Static3D orig = cubit.getOrigPosition();
+               Static4D quat1 = QUATS[quatIndex];
+               Static4D quat2 = QUATS[cubit.mQuatIndex];
+
+               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
+               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
+               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
+
+               float row1, row2;
+               float x1 = rotated1.get0();
+               float y1 = rotated1.get1();
+               float z1 = rotated1.get2();
+               float x2 = rotated2.get0();
+               float y2 = rotated2.get1();
+               float z2 = rotated2.get2();
+
+               for(int i=0; i<NUM_AXIS; i++)
+                 {
+                 row1 = computeRow(x1,y1,z1,i);
+                 row2 = computeRow(x2,y2,z2,i);
+
+                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return true;
+                 }
+               return false;
+
+      default: return false;  // edge or corner
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// only needed for solvers - there are no Ivy solvers ATM)
+
+  public String retObjectString()
+    {
+    return "";
+    }
+}
diff --git a/src/main/java/org/distorted/objects/TwistyBandagedFused.java b/src/main/java/org/distorted/objects/TwistyBandagedFused.java
new file mode 100644
index 00000000..d6e2746d
--- /dev/null
+++ b/src/main/java/org/distorted/objects/TwistyBandagedFused.java
@@ -0,0 +1,144 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2021 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube 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.                                                           //
+//                                                                                               //
+// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.objects;
+
+import android.content.res.Resources;
+
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshSquare;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.main.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class TwistyBandagedFused extends TwistyBandagedCube
+{
+  TwistyBandagedFused(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
+                      DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
+    {
+    super(size, quat, texture, mesh, effects, moves, ObjectList.BAN1, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitVariants()
+    {
+    return 2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getCubitVariant(int cubit)
+    {
+    return cubit==0 ? 0:1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubits()
+    {
+    return 20;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int[] getCubitDimensions(int variant)
+    {
+    return variant==0 ? new int[] {2,2,2} : new int[] {1,1,1};
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D getCubitPosition(int cubit)
+    {
+    switch(cubit)
+      {
+      case  0: return new Static3D(-0.5f, -0.5f, +0.5f);
+      case  1: return new Static3D(-1.0f, +1.0f, +1.0f);
+      case  2: return new Static3D(-1.0f, +1.0f, +0.0f);
+      case  3: return new Static3D(-1.0f, +1.0f, -1.0f);
+      case  4: return new Static3D( 0.0f, +1.0f, +1.0f);
+      case  5: return new Static3D( 0.0f, +1.0f, +0.0f);
+      case  6: return new Static3D( 0.0f, +1.0f, -1.0f);
+      case  7: return new Static3D( 1.0f, +1.0f, +1.0f);
+      case  8: return new Static3D( 1.0f, +1.0f, +0.0f);
+      case  9: return new Static3D( 1.0f, +1.0f, -1.0f);
+      case 10: return new Static3D( 1.0f,  0.0f, +1.0f);
+      case 11: return new Static3D( 1.0f,  0.0f, +0.0f);
+      case 12: return new Static3D( 1.0f,  0.0f, -1.0f);
+      case 13: return new Static3D( 1.0f, -1.0f, +1.0f);
+      case 14: return new Static3D( 1.0f, -1.0f, +0.0f);
+      case 15: return new Static3D( 1.0f, -1.0f, -1.0f);
+      case 16: return new Static3D(-1.0f, -1.0f, -1.0f);
+      case 17: return new Static3D(-1.0f,  0.0f, -1.0f);
+      case 18: return new Static3D( 0.0f, -1.0f, -1.0f);
+      case 19: return new Static3D( 0.0f,  0.0f, -1.0f);
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getQuatIndex(int cubit)
+    {
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+// TODO
+
+  public int getObjectName(int numLayers)
+    {
+    switch(numLayers)
+      {
+      case 2: return R.string.cube2;
+      case 3: return R.string.cube3;
+      case 4: return R.string.cube4;
+      case 5: return R.string.cube5;
+      }
+    return R.string.cube3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO
+
+  public int getInventor(int numLayers)
+    {
+    switch(numLayers)
+      {
+      case 2: return R.string.cube2_inventor;
+      case 3: return R.string.cube3_inventor;
+      case 4: return R.string.cube4_inventor;
+      case 5: return R.string.cube5_inventor;
+      }
+    return R.string.cube3_inventor;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getComplexity(int numLayers)
+    {
+    return 8;
+    }
+}
diff --git a/src/main/java/org/distorted/objects/TwistyObject.java b/src/main/java/org/distorted/objects/TwistyObject.java
index ff7e20bd..95630bb7 100644
--- a/src/main/java/org/distorted/objects/TwistyObject.java
+++ b/src/main/java/org/distorted/objects/TwistyObject.java
@@ -79,7 +79,7 @@ public abstract class TwistyObject extends DistortedNode
   private static final float MAX_SIZE_CHANGE = 1.35f;
   private static final float MIN_SIZE_CHANGE = 0.75f;
 
-  private static final boolean mCreateFromDMesh = true;
+  private static final boolean mCreateFromDMesh = false;
 
   private static final Static3D CENTER = new Static3D(0,0,0);
   private static final int POST_ROTATION_MILLISEC = 500;
