commit ece1b58dc6b84f9ef6dd3659bad24890cd90cf1a
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Sep 25 09:27:21 2020 +0100

    1) correction for Dino4's isSolved()  [ remember about the mirror! ]
    2) beginnings of support for the Skewb Diamond

diff --git a/src/main/java/org/distorted/main/RubikSurfaceView.java b/src/main/java/org/distorted/main/RubikSurfaceView.java
index e9966d94..2df60531 100644
--- a/src/main/java/org/distorted/main/RubikSurfaceView.java
+++ b/src/main/java/org/distorted/main/RubikSurfaceView.java
@@ -31,7 +31,6 @@ import android.view.MotionEvent;
 import com.google.firebase.crashlytics.FirebaseCrashlytics;
 
 import org.distorted.library.type.Static2D;
-import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
 import org.distorted.objects.RubikObject;
 import org.distorted.objects.RubikMovement;
diff --git a/src/main/java/org/distorted/objects/RubikCube.java b/src/main/java/org/distorted/objects/RubikCube.java
index d3367794..9d57b55a 100644
--- a/src/main/java/org/distorted/objects/RubikCube.java
+++ b/src/main/java/org/distorted/objects/RubikCube.java
@@ -65,9 +65,9 @@ class RubikCube extends RubikObject
 
   private static final int[] FACE_COLORS = new int[]
          {
-           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
-           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
-           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_BROWN
          };
 
   // All legal rotation quats of a RubikCube of any size.
diff --git a/src/main/java/org/distorted/objects/RubikDiamond.java b/src/main/java/org/distorted/objects/RubikDiamond.java
new file mode 100644
index 00000000..76dd0516
--- /dev/null
+++ b/src/main/java/org/distorted/objects/RubikDiamond.java
@@ -0,0 +1,404 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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 java.util.Random;
+
+import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikDiamond extends RubikObject
+{
+  private static final float SQ2 = (float)Math.sqrt(2);
+  private static final float SQ3 = (float)Math.sqrt(3);
+  private static final float SQ6 = (float)Math.sqrt(6);
+
+  private static final int FACES_PER_CUBIT =8;
+
+  // the four rotation axis of a Diamond. Must be normalized.
+  static final Static3D[] ROT_AXIS = new Static3D[]
+         {
+           new Static3D(+SQ6/3,+SQ3/3,     0),
+           new Static3D(-SQ6/3,+SQ3/3,     0),
+           new Static3D(     0,+SQ3/3,+SQ6/3),
+           new Static3D(     0,+SQ3/3,-SQ6/3)
+         };
+
+  // the eight axis that determine the faces
+  static final Static3D[] FACE_AXIS = new Static3D[]
+         {
+           new Static3D(+SQ6/3,+SQ3/3,     0), new Static3D(-SQ6/3,-SQ3/3,     0),
+           new Static3D(-SQ6/3,+SQ3/3,     0), new Static3D(+SQ6/3,-SQ3/3,     0),
+           new Static3D(     0,+SQ3/3,+SQ6/3), new Static3D(     0,-SQ3/3,-SQ6/3),
+           new Static3D(     0,+SQ3/3,-SQ6/3), new Static3D(     0,-SQ3/3,+SQ6/3)
+         };
+
+  private static final int[] FACE_COLORS = new int[]
+         {
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_BROWN,
+           COLOR_PINK  , COLOR_VIOLET
+         };
+
+  // All legal rotation quats of a Diamond
+  private static final Static4D[] QUATS = new Static4D[]
+         {
+           new Static4D(  0.0f,  0.0f,   0.0f,  1.0f ),
+           new Static4D(  0.0f,  1.0f,   0.0f,  0.0f ),
+           new Static4D(+SQ2/2,  0.5f,   0.0f,  0.5f ),
+           new Static4D(-SQ2/2,  0.5f,   0.0f,  0.5f ),
+           new Static4D(  0.0f,  0.5f, +SQ2/2,  0.5f ),
+           new Static4D(  0.0f,  0.5f, -SQ2/2,  0.5f ),
+           new Static4D(+SQ2/2,  0.5f,   0.0f, -0.5f ),
+           new Static4D(-SQ2/2,  0.5f,   0.0f, -0.5f ),
+           new Static4D(  0.0f,  0.5f, +SQ2/2, -0.5f ),
+           new Static4D(  0.0f,  0.5f, -SQ2/2, -0.5f ),
+           new Static4D(+SQ2/2,  0.0f, -SQ2/2,  0.0f ),
+           new Static4D(-SQ2/2,  0.0f, -SQ2/2,  0.0f )
+         };
+
+  private static final float DIST = 0.50f;
+
+  // centers of the 6 octahedrons + 8 tetrahedrons ( i.e. of the all 14 cubits)
+  private static final Static3D[] CENTERS = new Static3D[]
+         {
+           new Static3D( DIST,          0, DIST ),
+           new Static3D( DIST,          0,-DIST ),
+           new Static3D(-DIST,          0,-DIST ),
+           new Static3D(-DIST,          0, DIST ),
+           new Static3D(    0, DIST*SQ2  ,    0 ),
+           new Static3D(    0,-DIST*SQ2  ,    0 ),
+
+           new Static3D(    0, DIST*SQ2/2, DIST ),
+           new Static3D( DIST, DIST*SQ2/2,    0 ),
+           new Static3D(    0, DIST*SQ2/2,-DIST ),
+           new Static3D(-DIST, DIST*SQ2/2,    0 ),
+           new Static3D(    0,-DIST*SQ2/2, DIST ),
+           new Static3D( DIST,-DIST*SQ2/2,    0 ),
+           new Static3D(    0,-DIST*SQ2/2,-DIST ),
+           new Static3D(-DIST,-DIST*SQ2/2,    0 )
+         };
+
+  // Colors of the faces of cubits. Each cubit has 8 faces
+  private static final int[][] mFaceMap = new int[][]
+         {
+           { 6,1,8,8, 2,5,8,8 },
+           { 8,1,3,8, 8,5,7,8 },
+           { 8,8,3,4, 8,8,7,0 },
+           { 6,8,8,4, 2,8,8,0 },
+           { 6,1,3,4, 8,8,8,8 },
+           { 8,8,8,8, 2,5,7,0 },
+
+           { 6,8,8,8, 8,8,8,8 },
+           { 1,8,8,8, 8,8,8,8 },
+           { 3,8,8,8, 8,8,8,8 },
+           { 4,8,8,8, 8,8,8,8 },
+           { 2,8,8,8, 8,8,8,8 },
+           { 5,8,8,8, 8,8,8,8 },
+           { 7,8,8,8, 8,8,8,8 },
+           { 0,8,8,8, 8,8,8,8 }
+         };
+
+  private static MeshBase mOctaMesh, mTetraMesh;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  RubikDiamond(int size, Static4D quat, DistortedTexture texture,
+               MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
+    {
+    super(size, 60, quat, texture, mesh, effects, moves, RubikObjectList.DIAM, res, scrWidth);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createOctaMesh()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createTetraMesh()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float getScreenRatio()
+    {
+    return 1.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static4D[] getQuats()
+    {
+    return QUATS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumFaces()
+    {
+    return FACE_COLORS.length;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean shouldResetTextureMaps()
+    {
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumStickerTypes()
+    {
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float getBasicStep()
+    {
+    return SQ6/6;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getNumCubitFaces()
+    {
+    return FACES_PER_CUBIT;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  Static3D[] getCubitPositions(int size)
+    {
+    return CENTERS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private Static4D getQuat(int cubit)
+    {
+    switch(cubit)
+      {
+      case  0:
+      case  1:
+      case  2:
+      case  3:
+      case  4:
+      case  5:
+      case  6: return QUATS[0];                          // unit quat
+      case  7: return new Static4D( SQ2/2,0,0,SQ2/2);    //  90 along Y
+      case  8: return QUATS[1];                          // 180 along Y
+      case  9: return new Static4D(-SQ2/2,0,0,SQ2/2);    //  90 along Y
+      case 10: return new Static4D(     0,0,1,    0);    // 180 along Z
+      case 11: return new Static4D(0, SQ2/2,SQ2/2,0);    //
+      case 12: return new Static4D(     1,0,0,    0);    // 180 along X
+      case 13: return new Static4D(0,-SQ2/2,SQ2/2,0);    //
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  MeshBase createCubitMesh(int cubit)
+    {
+    MeshBase mesh;
+
+    if( cubit<6 )
+      {
+      if( mOctaMesh==null ) createOctaMesh();
+      mesh = mOctaMesh.copy(true);
+      }
+    else
+      {
+      if( mTetraMesh==null ) createTetraMesh();
+      mesh = mTetraMesh.copy(true);
+      }
+
+    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( getQuat(cubit), new Static3D(0,0,0) );
+    mesh.apply(quat,0xffffffff,0);
+
+    return mesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int getFaceColor(int cubit, int cubitface, int size)
+    {
+    return mFaceMap[cubit][cubitface];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
+    {
+    float STROKE = 0.044f*side;
+    float OFF = STROKE/2 -1;
+    float OFF2 = 0.5f*side + OFF;
+    float HEIGHT = side - OFF;
+    float RADIUS = side/12.0f;
+    float ARC1_H = 0.2f*side;
+    float ARC1_W = side*0.5f;
+    float ARC2_W = 0.153f*side;
+    float ARC2_H = 0.905f*side;
+    float ARC3_W = side-ARC2_W;
+
+    float M = SQ3/2;
+    float D = (M/2 - 0.51f)*side;
+
+    paint.setAntiAlias(true);
+    paint.setStrokeWidth(STROKE);
+    paint.setColor(FACE_COLORS[face]);
+    paint.setStyle(Paint.Style.FILL);
+
+    canvas.drawRect(left,top,left+side,top+side,paint);
+
+    paint.setColor(INTERIOR_COLOR);
+    paint.setStyle(Paint.Style.STROKE);
+
+    canvas.drawLine(           left, M*HEIGHT+D,  side       +left, M*HEIGHT+D, paint);
+    canvas.drawLine(      OFF +left, M*side  +D,       OFF2  +left,          D, paint);
+    canvas.drawLine((side-OFF)+left, M*side  +D, (side-OFF2) +left,          D, paint);
+
+    canvas.drawArc( ARC1_W-RADIUS+left, M*(ARC1_H-RADIUS)+D, ARC1_W+RADIUS+left, M*(ARC1_H+RADIUS)+D, 225, 90, false, paint);
+    canvas.drawArc( ARC2_W-RADIUS+left, M*(ARC2_H-RADIUS)+D, ARC2_W+RADIUS+left, M*(ARC2_H+RADIUS)+D, 105, 90, false, paint);
+    canvas.drawArc( ARC3_W-RADIUS+left, M*(ARC2_H-RADIUS)+D, ARC3_W+RADIUS+left, M*(ARC2_H+RADIUS)+D, 345, 90, false, paint);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float returnMultiplier()
+    {
+    return 2.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  float[] getRowChances()
+    {
+    float[] chances = new float[2];
+
+    chances[0] = 0.5f;
+    chances[1] = 1.0f;
+
+    return chances;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public Static3D[] getRotationAxis()
+    {
+    return ROT_AXIS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getBasicAngle()
+    {
+    return 3;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int computeRowFromOffset(float offset)
+    {
+    return offset<0.25f ? 0:1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float returnRotationFactor(float offset)
+    {
+    return 1.0f;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  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;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// The Diamond is solved if and only if:
+//
+// ??
+
+  public boolean isSolved()
+    {
+
+
+    return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// only needed for solvers - there are no Diamond solvers ATM)
+
+  public String retObjectString()
+    {
+    return "";
+    }
+
+}
diff --git a/src/main/java/org/distorted/objects/RubikDino.java b/src/main/java/org/distorted/objects/RubikDino.java
index a5eae07f..4f927453 100644
--- a/src/main/java/org/distorted/objects/RubikDino.java
+++ b/src/main/java/org/distorted/objects/RubikDino.java
@@ -37,6 +37,7 @@ import org.distorted.library.mesh.MeshSquare;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
+import org.distorted.main.RubikSurfaceView;
 
 import java.util.Random;
 
@@ -68,9 +69,9 @@ public abstract class RubikDino extends RubikObject
 
   private static final int[] FACE_COLORS = new int[]
          {
-           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
-           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
-           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_BROWN
          };
 
   // All legal rotation quats of a RubikDino
@@ -245,6 +246,46 @@ public abstract class RubikDino extends RubikObject
     mMesh.mergeEffComponents();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  int mulQuat(int q1, int q2)
+    {
+    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
+
+    float rX = result.get0();
+    float rY = result.get1();
+    float rZ = result.get2();
+    float rW = result.get3();
+
+    final float MAX_ERROR = 0.1f;
+    float dX,dY,dZ,dW;
+
+    for(int i=0; i<QUATS.length; i++)
+      {
+      dX = QUATS[i].get0() - rX;
+      dY = QUATS[i].get1() - rY;
+      dZ = QUATS[i].get2() - rZ;
+      dW = QUATS[i].get3() - rW;
+
+      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
+          dY<MAX_ERROR && dY>-MAX_ERROR &&
+          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
+          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
+
+      dX = QUATS[i].get0() + rX;
+      dY = QUATS[i].get1() + rY;
+      dZ = QUATS[i].get2() + rZ;
+      dW = QUATS[i].get3() + rW;
+
+      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
+          dY<MAX_ERROR && dY>-MAX_ERROR &&
+          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
+          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
+      }
+
+    return -1;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   float getScreenRatio()
diff --git a/src/main/java/org/distorted/objects/RubikDino4.java b/src/main/java/org/distorted/objects/RubikDino4.java
index d374a3b5..2546ba36 100644
--- a/src/main/java/org/distorted/objects/RubikDino4.java
+++ b/src/main/java/org/distorted/objects/RubikDino4.java
@@ -73,23 +73,39 @@ public class RubikDino4 extends RubikDino
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Dino4 is solved if and only if the four groups of three same-colored cubits each are rotate with
-// the same quaternion (actually we need to check only 3 first groups - if those are correct, the
-// fourth one also needs to be correct).
+// Dino4 is solved if and only if the four groups of three same-colored cubits are each 'together'
+// (actually we need to check only 3 first groups - if those are correct, the fourth one also needs
+// to be correct).
 //
-// White group : 6,10,11
-// Red group   : 0,3,7
-// Blue group  : 1,2,5
-// Yellow group: 4,8,9
+// White group : (X,Y,Z) = 10,11,6
+// Red group   : (X,Y,Z) = 0,3,7
+// Blue group  : (X,Y,Z) = 2,1,5
+// Yellow group: (X,Y,Z) = 8,9,4
+//
+// A group of 3 cubits is 'together' if and only if they are all rotated with one quat - but we cannot
+// forget that the whole Dino can be mirrored! (so then qY = qX*Q2 and qZ = qX*Q8 )
+//
+// X cubits: 0, 2, 8, 10
+// Y cubits: 1, 3, 9, 11
+// Z cubits: 4, 5, 6, 7
 
   public boolean isSolved()
     {
-    int qR = CUBITS[0].mQuatIndex;
-    int qB = CUBITS[1].mQuatIndex;
-    int qY = CUBITS[4].mQuatIndex;
+    int redX = CUBITS[0].mQuatIndex;
+    int bluX = CUBITS[2].mQuatIndex;
+    int yelX = CUBITS[8].mQuatIndex;
+
+    if (CUBITS[3].mQuatIndex == redX && CUBITS[7].mQuatIndex == redX &&
+        CUBITS[1].mQuatIndex == bluX && CUBITS[5].mQuatIndex == bluX &&
+        CUBITS[9].mQuatIndex == yelX && CUBITS[4].mQuatIndex == yelX  ) return true;
 
-    return (CUBITS[3].mQuatIndex == qR && CUBITS[7].mQuatIndex == qR &&
-            CUBITS[2].mQuatIndex == qB && CUBITS[5].mQuatIndex == qB &&
-            CUBITS[8].mQuatIndex == qY && CUBITS[9].mQuatIndex == qY  );
+    if (CUBITS[3].mQuatIndex != mulQuat(redX,2)) return false;
+    if (CUBITS[7].mQuatIndex != mulQuat(redX,8)) return false;
+    if (CUBITS[1].mQuatIndex != mulQuat(bluX,2)) return false;
+    if (CUBITS[5].mQuatIndex != mulQuat(bluX,8)) return false;
+    if (CUBITS[9].mQuatIndex != mulQuat(yelX,2)) return false;
+    if (CUBITS[4].mQuatIndex != mulQuat(yelX,8)) return false;
+
+    return true;
     }
 }
diff --git a/src/main/java/org/distorted/objects/RubikDino6.java b/src/main/java/org/distorted/objects/RubikDino6.java
index 5822ef3f..9f3040b2 100644
--- a/src/main/java/org/distorted/objects/RubikDino6.java
+++ b/src/main/java/org/distorted/objects/RubikDino6.java
@@ -47,46 +47,6 @@ public class RubikDino6 extends RubikDino
     super(size, quat, texture, mesh, effects, moves, RubikObjectList.DINO, res, scrWidth);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int mulQuat(int q1, int q2)
-    {
-    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
-
-    float rX = result.get0();
-    float rY = result.get1();
-    float rZ = result.get2();
-    float rW = result.get3();
-
-    final float MAX_ERROR = 0.1f;
-    float dX,dY,dZ,dW;
-
-    for(int i=0; i<QUATS.length; i++)
-      {
-      dX = QUATS[i].get0() - rX;
-      dY = QUATS[i].get1() - rY;
-      dZ = QUATS[i].get2() - rZ;
-      dW = QUATS[i].get3() - rW;
-
-      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
-          dY<MAX_ERROR && dY>-MAX_ERROR &&
-          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
-          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
-
-      dX = QUATS[i].get0() + rX;
-      dY = QUATS[i].get1() + rY;
-      dZ = QUATS[i].get2() + rZ;
-      dW = QUATS[i].get3() + rW;
-
-      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
-          dY<MAX_ERROR && dY>-MAX_ERROR &&
-          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
-          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
-      }
-
-    return -1;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   int getFaceColor(int cubit, int cubitface, int size)
diff --git a/src/main/java/org/distorted/objects/RubikHelicopter.java b/src/main/java/org/distorted/objects/RubikHelicopter.java
index db902f56..704a2815 100644
--- a/src/main/java/org/distorted/objects/RubikHelicopter.java
+++ b/src/main/java/org/distorted/objects/RubikHelicopter.java
@@ -74,9 +74,9 @@ public class RubikHelicopter extends RubikObject
 
   private static final int[] FACE_COLORS = new int[]
          {
-           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
-           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
-           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_BROWN
          };
 
   // All legal rotation quats of a HELICOPTER (same as the Cube!)
diff --git a/src/main/java/org/distorted/objects/RubikMovementDiamond.java b/src/main/java/org/distorted/objects/RubikMovementDiamond.java
new file mode 100644
index 00000000..b8c1961f
--- /dev/null
+++ b/src/main/java/org/distorted/objects/RubikMovementDiamond.java
@@ -0,0 +1,116 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 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;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class RubikMovementDiamond extends RubikMovement
+{
+  RubikMovementDiamond()
+    {
+    super(RubikDiamond.ROT_AXIS, RubikDiamond.FACE_AXIS, 0.25f, 0.25f);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// _____________
+// |  \  0  /  |
+// |   \   /   |
+// | 3 |   | 1 |
+// |   /   \   |
+// |  /  2  \  |
+// -------------
+
+  private int getQuarter(float[] touchPoint)
+    {
+    boolean p0 = touchPoint[1] >= touchPoint[0];
+    boolean p1 = touchPoint[1] >=-touchPoint[0];
+
+    if( p0 )  return p1 ? 0:3;
+    else      return p1 ? 1:2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean isInsideFace(float[] p)
+    {
+    return ( p[0]<=0.25f && p[0]>=-0.25f && p[1]<=0.25f && p[1]>=-0.25f );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void computeEnabledAxis(int face, float[] touchPoint, int[] enabled)
+    {
+    enabled[0] = 2;
+
+    int quarter = getQuarter(touchPoint);
+
+    switch(face)
+      {
+      case 0: switch(quarter)
+                {
+                case 0: enabled[1]=0; enabled[2]=1; break;
+                case 1: enabled[1]=3; enabled[2]=1; break;
+                case 2: enabled[1]=2; enabled[2]=3; break;
+                case 3: enabled[1]=0; enabled[2]=2; break;
+                }
+              break;
+      case 1: switch(quarter)
+                {
+                case 0: enabled[1]=2; enabled[2]=3; break;
+                case 1: enabled[1]=3; enabled[2]=1; break;
+                case 2: enabled[1]=0; enabled[2]=1; break;
+                case 3: enabled[1]=0; enabled[2]=2; break;
+                }
+              break;
+      case 2: switch(quarter)
+                {
+                case 0: enabled[1]=1; enabled[2]=2; break;
+                case 1: enabled[1]=0; enabled[2]=1; break;
+                case 2: enabled[1]=0; enabled[2]=3; break;
+                case 3: enabled[1]=2; enabled[2]=3; break;
+                }
+              break;
+      case 3: switch(quarter)
+                {
+                case 0: enabled[1]=1; enabled[2]=2; break;
+                case 1: enabled[1]=2; enabled[2]=3; break;
+                case 2: enabled[1]=0; enabled[2]=3; break;
+                case 3: enabled[1]=0; enabled[2]=1; break;
+                }
+              break;
+      case 4: switch(quarter)
+                {
+                case 0: enabled[1]=0; enabled[2]=3; break;
+                case 1: enabled[1]=0; enabled[2]=2; break;
+                case 2: enabled[1]=1; enabled[2]=2; break;
+                case 3: enabled[1]=1; enabled[2]=3; break;
+                }
+              break;
+      case 5: switch(quarter)
+                {
+                case 0: enabled[1]=1; enabled[2]=2; break;
+                case 1: enabled[1]=0; enabled[2]=2; break;
+                case 2: enabled[1]=0; enabled[2]=3; break;
+                case 3: enabled[1]=1; enabled[2]=3; break;
+                }
+              break;
+      }
+    }
+}
diff --git a/src/main/java/org/distorted/objects/RubikObject.java b/src/main/java/org/distorted/objects/RubikObject.java
index 88b203e8..6f46302f 100644
--- a/src/main/java/org/distorted/objects/RubikObject.java
+++ b/src/main/java/org/distorted/objects/RubikObject.java
@@ -55,6 +55,15 @@ import java.util.Random;
 
 public abstract class RubikObject extends DistortedNode
   {
+  static final int COLOR_YELLOW = 0xffffff00;
+  static final int COLOR_WHITE  = 0xffffffff;
+  static final int COLOR_BLUE   = 0xff0000ff;
+  static final int COLOR_GREEN  = 0xff00ff00;
+  static final int COLOR_RED    = 0xffff0000;
+  static final int COLOR_BROWN  = 0xffb5651d;
+  static final int COLOR_PINK   = 0xffe134eb;
+  static final int COLOR_VIOLET = 0xffa534eb;
+
   private static final float NODE_RATIO = 1.32f;
   private static final float MAX_SIZE_CHANGE = 1.3f;
   private static final float MIN_SIZE_CHANGE = 0.8f;
diff --git a/src/main/java/org/distorted/objects/RubikObjectList.java b/src/main/java/org/distorted/objects/RubikObjectList.java
index a7084695..f3e3f6f5 100644
--- a/src/main/java/org/distorted/objects/RubikObjectList.java
+++ b/src/main/java/org/distorted/objects/RubikObjectList.java
@@ -57,6 +57,15 @@ public enum RubikObjectList
          1
        ),
 
+  DIAM (
+         new int[][] {
+                       {2 , 10, R.raw.dino, R.drawable.ui_small_dino, R.drawable.ui_medium_dino, R.drawable.ui_big_dino, R.drawable.ui_huge_dino} ,
+                     },
+         RubikDiamond.class,
+         new RubikMovementDiamond(),
+         1
+       ),
+
   DINO (
          new int[][] {
                        {3 , 10, R.raw.dino, R.drawable.ui_small_dino, R.drawable.ui_medium_dino, R.drawable.ui_big_dino, R.drawable.ui_huge_dino} ,
@@ -466,10 +475,11 @@ public enum RubikObjectList
       {
       case 0: return new RubikCube      (size, quat, texture, mesh, effects, moves, res, scrWidth);
       case 1: return new RubikPyraminx  (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 2: return new RubikDino6     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 3: return new RubikDino4     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 4: return new RubikSkewb     (size, quat, texture, mesh, effects, moves, res, scrWidth);
-      case 5: return new RubikHelicopter(size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 2: return new RubikDiamond   (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 3: return new RubikDino6     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 4: return new RubikDino4     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 5: return new RubikSkewb     (size, quat, texture, mesh, effects, moves, res, scrWidth);
+      case 6: return new RubikHelicopter(size, quat, texture, mesh, effects, moves, res, scrWidth);
       }
 
     return null;
diff --git a/src/main/java/org/distorted/objects/RubikPyraminx.java b/src/main/java/org/distorted/objects/RubikPyraminx.java
index 67f6a4dc..96e654b1 100644
--- a/src/main/java/org/distorted/objects/RubikPyraminx.java
+++ b/src/main/java/org/distorted/objects/RubikPyraminx.java
@@ -68,8 +68,8 @@ public class RubikPyraminx extends RubikObject
 
   private static final int[] FACE_COLORS = new int[]
          {
-           0xff00ff00, 0xffffff00,  // FACE_AXIS[0] (GREEN ) FACE_AXIS[1] (YELLOW )
-           0xff0000ff, 0xffff0000   // FACE_AXIS[2] (BLUE  ) FACE_AXIS[3] (RED    )
+           COLOR_GREEN , COLOR_YELLOW,
+           COLOR_BLUE  , COLOR_RED
          };
 
   // computed with res/raw/compute_quats.c
diff --git a/src/main/java/org/distorted/objects/RubikSkewb.java b/src/main/java/org/distorted/objects/RubikSkewb.java
index 538b6054..7b0e07de 100644
--- a/src/main/java/org/distorted/objects/RubikSkewb.java
+++ b/src/main/java/org/distorted/objects/RubikSkewb.java
@@ -72,9 +72,9 @@ public class RubikSkewb extends RubikObject
 
   private static final int[] FACE_COLORS = new int[]
          {
-           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
-           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
-           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
+           COLOR_YELLOW, COLOR_WHITE,
+           COLOR_BLUE  , COLOR_GREEN,
+           COLOR_RED   , COLOR_BROWN
          };
 
   // All legal rotation quats of a RubikSkewb
diff --git a/src/main/res/raw/compute_quats.c b/src/main/res/raw/compute_quats.c
index 0778b470..ca64ede1 100644
--- a/src/main/res/raw/compute_quats.c
+++ b/src/main/res/raw/compute_quats.c
@@ -2,7 +2,7 @@
 #include <math.h>
 #include <stdlib.h>
 
-#define DINO
+#define DIAM
 
 #define SQ2 1.41421356237f
 #define SQ3 1.73205080757f
@@ -38,6 +38,16 @@ float axis[NUM_AXIS][3] = { {+SQ3/3,+SQ3/3,+SQ3/3} ,
                             {+SQ3/3,-SQ3/3,-SQ3/3} };
 #endif
 
+#ifdef DIAM
+#define NUM_AXIS    4
+#define BASIC_ANGLE 3
+
+float axis[NUM_AXIS][3] = { {+SQ3*SQ2/3,+SQ3/3,         0} ,
+                            {-SQ3*SQ2/3,+SQ3/3,         0} ,
+                            {         0,+SQ3/3,+SQ3*SQ2/3} ,
+                            {         0,+SQ3/3,-SQ3*SQ2/3} };
+#endif
+
 #ifdef HELI
 #define NUM_AXIS 6
 #define BASIC_ANGLE 2
