commit 7289fd6ce4e14d29e53831ce009af822b9629d36
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Apr 3 22:40:35 2020 +0100

    Beginnings of support for the 3x3x3 Solver.

diff --git a/src/main/java/org/distorted/magic/RubikActivity.java b/src/main/java/org/distorted/magic/RubikActivity.java
index 70ca5f00..b45382b4 100644
--- a/src/main/java/org/distorted/magic/RubikActivity.java
+++ b/src/main/java/org/distorted/magic/RubikActivity.java
@@ -252,7 +252,7 @@ public class RubikActivity extends AppCompatActivity
 
     public void Solver(View v)
       {
-      android.util.Log.e("act", "Not implemented yet");
+      RubikState.switchState(this,RubikState.SVER);
       }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/object/RubikCube.java b/src/main/java/org/distorted/object/RubikCube.java
index 7cc63a79..1069f65c 100644
--- a/src/main/java/org/distorted/object/RubikCube.java
+++ b/src/main/java/org/distorted/object/RubikCube.java
@@ -83,6 +83,19 @@ class RubikCube extends RubikObject
     super(size, 60, quatCur,quatAcc,texture,mesh,effects,moves, RubikObjectList.CUBE);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// paint the square with upper-right cornder at (left,top) and side length 'side' with texture
+// for face 'face'.
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
+    {
+    final float R = side*0.10f;
+    final float M = side*0.05f;
+
+    paint.setColor(FACE_COLORS[face]);
+    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   Static3D[] getCubitPositions(int size)
@@ -145,19 +158,6 @@ class RubikCube extends RubikObject
     return new VertexEffectSink( new Static1D(strength), center, region );
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// paint only the square with lower-left corner at (face*TEX_H,0) and side length TEX_H
-
-  void createFaceTexture(Canvas canvas, Paint paint, int face)
-    {
-    final int S = TEXTURE_HEIGHT;
-    final int R = TEXTURE_HEIGHT/10;
-    final int M = TEXTURE_HEIGHT/20;
-
-    paint.setColor(FACE_COLORS[face]);
-    canvas.drawRoundRect( (face*S+M), M, (face*S+M) + (S-2*M), M + (S-2*M), R, R, paint);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createCubitMesh(int cubit, int vertices)
diff --git a/src/main/java/org/distorted/object/RubikObject.java b/src/main/java/org/distorted/object/RubikObject.java
index 91e26f1a..3c0e493c 100644
--- a/src/main/java/org/distorted/object/RubikObject.java
+++ b/src/main/java/org/distorted/object/RubikObject.java
@@ -45,7 +45,7 @@ public abstract class RubikObject extends DistortedNode
   {
   public static final int NODE_FBO_SIZE = 600;
 
-  static final int TEXTURE_HEIGHT = 128;
+  private static final int TEXTURE_HEIGHT = 128;
   final float[] LEGAL_QUATS;
   final Static3D[] ROTATION_AXIS;
 
@@ -312,7 +312,7 @@ public abstract class RubikObject extends DistortedNode
 
     for(int i=0; i<numColors; i++)
       {
-      createFaceTexture(canvas,paint,i);
+      createFaceTexture(canvas,paint,i, i*TEXTURE_HEIGHT, 0, TEXTURE_HEIGHT);
       }
 
     mTexture.setTexture(bitmap);
@@ -526,8 +526,8 @@ public abstract class RubikObject extends DistortedNode
   abstract Static3D[] getCubitPositions(int size);
   abstract float[] getLegalQuats();
   abstract int getNumFaces();
-  abstract void createFaceTexture(Canvas canvas, Paint paint, int face);
   abstract MeshBase createCubitMesh(int cubit, int vertices);
+  abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side);
   public abstract Static3D[] getRotationAxis();
   public abstract int getBasicAngle();
   public abstract int returnRowFromOffset(float offset);
diff --git a/src/main/java/org/distorted/object/RubikPyraminx.java b/src/main/java/org/distorted/object/RubikPyraminx.java
index db548334..3658f42b 100644
--- a/src/main/java/org/distorted/object/RubikPyraminx.java
+++ b/src/main/java/org/distorted/object/RubikPyraminx.java
@@ -208,41 +208,6 @@ public class RubikPyraminx extends RubikObject
     return new VertexEffectSink( new Static1D(1.3f), center, region );
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void createFaceTexture(Canvas canvas, Paint paint, int face)
-    {
-    int xoffset = face*TEXTURE_HEIGHT;
-    float STROKE = 0.06f*TEXTURE_HEIGHT;
-    float OFF = STROKE/2 -1;
-    float OFF2 = 0.5f*TEXTURE_HEIGHT + OFF;
-    float HEIGHT = TEXTURE_HEIGHT - OFF;
-    float RADIUS = TEXTURE_HEIGHT/12.0f;
-    float ARC1_H = 0.2f*TEXTURE_HEIGHT;
-    float ARC1_W = TEXTURE_HEIGHT*0.5f;
-    float ARC2_W = 0.153f*TEXTURE_HEIGHT;
-    float ARC2_H = 0.905f*TEXTURE_HEIGHT;
-    float ARC3_W = TEXTURE_HEIGHT-ARC2_W;
-
-    paint.setAntiAlias(true);
-    paint.setStrokeWidth(STROKE);
-    paint.setColor(FACE_COLORS[face]);
-    paint.setStyle(Paint.Style.FILL);
-
-    canvas.drawRect(xoffset,0,xoffset+TEXTURE_HEIGHT,TEXTURE_HEIGHT,paint);
-
-    paint.setColor(0xff000000);
-    paint.setStyle(Paint.Style.STROKE);
-
-    canvas.drawLine(                     xoffset,         HEIGHT,  TEXTURE_HEIGHT       +xoffset, HEIGHT, paint);
-    canvas.drawLine(                OFF +xoffset, TEXTURE_HEIGHT,                 OFF2  +xoffset,      0, paint);
-    canvas.drawLine((TEXTURE_HEIGHT-OFF)+xoffset, TEXTURE_HEIGHT, (TEXTURE_HEIGHT-OFF2) +xoffset,      0, paint);
-
-    canvas.drawArc( ARC1_W-RADIUS+xoffset, ARC1_H-RADIUS, ARC1_W+RADIUS+xoffset, ARC1_H+RADIUS, 225, 90, false, paint);
-    canvas.drawArc( ARC2_W-RADIUS+xoffset, ARC2_H-RADIUS, ARC2_W+RADIUS+xoffset, ARC2_H+RADIUS, 105, 90, false, paint);
-    canvas.drawArc( ARC3_W-RADIUS+xoffset, ARC2_H-RADIUS, ARC3_W+RADIUS+xoffset, ARC2_H+RADIUS, 345, 90, false, paint);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   MeshBase createCubitMesh(int cubit, int vertices)
@@ -289,6 +254,40 @@ public class RubikPyraminx extends RubikObject
     return result;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
+    {
+    float STROKE = 0.06f*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;
+
+    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(0xff000000);
+    paint.setStyle(Paint.Style.STROKE);
+
+    canvas.drawLine(           left, HEIGHT,  side       +left, HEIGHT, paint);
+    canvas.drawLine(      OFF +left, side  ,       OFF2  +left,      0, paint);
+    canvas.drawLine((side-OFF)+left, side  , (side-OFF2) +left,      0, paint);
+
+    canvas.drawArc( ARC1_W-RADIUS+left, ARC1_H-RADIUS, ARC1_W+RADIUS+left, ARC1_H+RADIUS, 225, 90, false, paint);
+    canvas.drawArc( ARC2_W-RADIUS+left, ARC2_H-RADIUS, ARC2_W+RADIUS+left, ARC2_H+RADIUS, 105, 90, false, paint);
+    canvas.drawArc( ARC3_W-RADIUS+left, ARC2_H-RADIUS, ARC3_W+RADIUS+left, ARC2_H+RADIUS, 345, 90, false, paint);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 
diff --git a/src/main/java/org/distorted/uistate/RubikState.java b/src/main/java/org/distorted/uistate/RubikState.java
index 0809cf1e..4bd475bb 100644
--- a/src/main/java/org/distorted/uistate/RubikState.java
+++ b/src/main/java/org/distorted/uistate/RubikState.java
@@ -26,10 +26,11 @@ import org.distorted.magic.RubikActivity;
 
 public enum RubikState
   {
-  MAIN ( null , true , new RubikStateMain()    ),
+  MAIN ( null , false, new RubikStateMain()    ),
   PLAY ( MAIN , true , new RubikStatePlay()    ),
   SOLV ( PLAY , true , new RubikStateSolving() ),
   PATT ( MAIN , false, new RubikStatePattern() ),
+  SVER ( MAIN , false, new RubikStateSolver()  ),
   ;
 
   public static final int LENGTH = values().length;
diff --git a/src/main/java/org/distorted/uistate/RubikStateMain.java b/src/main/java/org/distorted/uistate/RubikStateMain.java
index 975f1fb3..b40fa209 100644
--- a/src/main/java/org/distorted/uistate/RubikStateMain.java
+++ b/src/main/java/org/distorted/uistate/RubikStateMain.java
@@ -67,7 +67,7 @@ public class RubikStateMain extends RubikStateAbstract
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
-    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
     layoutTop.removeAllViews();
     final TextView text = (TextView)inflater.inflate(R.layout.upper_text, null);
     text.setText(R.string.app_name);
diff --git a/src/main/java/org/distorted/uistate/RubikStatePattern.java b/src/main/java/org/distorted/uistate/RubikStatePattern.java
index 6cfa181a..cc9e4c0f 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePattern.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePattern.java
@@ -98,7 +98,7 @@ public class RubikStatePattern extends RubikStateAbstract
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
-    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
     layoutTop.removeAllViews();
     mText = (TextView)inflater.inflate(R.layout.upper_pattern_text, null);
     mText.setText(R.string.patterns);
diff --git a/src/main/java/org/distorted/uistate/RubikStatePlay.java b/src/main/java/org/distorted/uistate/RubikStatePlay.java
index bd3309aa..616c1d67 100644
--- a/src/main/java/org/distorted/uistate/RubikStatePlay.java
+++ b/src/main/java/org/distorted/uistate/RubikStatePlay.java
@@ -72,7 +72,7 @@ public class RubikStatePlay extends RubikStateAbstract
     // TOP ////////////////////////////
     final View viewTop = inflater.inflate(R.layout.play_title, null);
 
-    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
     layoutTop.removeAllViews();
     layoutTop.addView(viewTop);
 
diff --git a/src/main/java/org/distorted/uistate/RubikStateSolver.java b/src/main/java/org/distorted/uistate/RubikStateSolver.java
new file mode 100644
index 00000000..99be3a9c
--- /dev/null
+++ b/src/main/java/org/distorted/uistate/RubikStateSolver.java
@@ -0,0 +1,181 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.uistate;
+
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import org.distorted.magic.R;
+import org.distorted.magic.RubikActivity;
+import org.distorted.object.RubikObjectList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class RubikStateSolver extends RubikStateAbstract
+  {
+  private static final int NUM_FACES   =  6;
+  private static final int BITMAP_SIZE = 35;
+
+  private static final int[] FACE_COLORS = new int[]
+         {
+           0xffffff00, 0xffffffff,
+           0xff0000ff, 0xff00ff00,
+           0xffff0000, 0xffb5651d
+         };
+
+  private static Bitmap[] mBitmap;
+  private ImageButton[] mColorButton;
+  private Button mBackButton;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void leaveState(RubikActivity act)
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void enterState(final RubikActivity act)
+    {
+    act.changeObject(RubikObjectList.CUBE,3,null);
+
+    DisplayMetrics metrics = act.getResources().getDisplayMetrics();
+    final float scale = metrics.density;
+
+    // TOP ////////////////////////////
+    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
+    layoutTop.removeAllViews();
+
+    if( mBitmap     ==null ) setupBitmaps(scale);
+    if( mColorButton==null ) setupColorButtons(act,scale);
+
+    for(ImageButton button: mColorButton) layoutTop.addView(button);
+
+    // BOT ////////////////////////////
+    if( mBackButton==null ) setupBackButton(act,scale);
+
+    LinearLayout layoutRight = act.findViewById(R.id.mainBarRight);
+    layoutRight.removeAllViews();
+    layoutRight.addView(mBackButton);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side);
+
+  private void setupBitmaps(float scale)
+    {
+    final int SIZE = (int)(scale*BITMAP_SIZE);
+    final float R = SIZE*0.10f;
+    final float M = SIZE*0.05f;
+
+    mBitmap = new Bitmap[NUM_FACES];
+
+    Paint paint = new Paint();
+    paint.setColor(0xff008800);
+    paint.setStyle(Paint.Style.FILL);
+
+    paint.setAntiAlias(true);
+    paint.setTextAlign(Paint.Align.CENTER);
+    paint.setStyle(Paint.Style.FILL);
+
+    for(int i=0; i<NUM_FACES; i++)
+      {
+      mBitmap[i] = Bitmap.createBitmap(SIZE, SIZE, Bitmap.Config.ARGB_8888);
+      Canvas canvas = new Canvas(mBitmap[i]);
+
+      paint.setColor(0xff000000);
+      canvas.drawRect(0, 0, SIZE, SIZE, paint);
+
+      paint.setColor(FACE_COLORS[i]);
+      canvas.drawRoundRect( M, M, SIZE-M, SIZE-M, R, R, paint);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupColorButtons(final RubikActivity act, final float scale)
+    {
+    mColorButton = new ImageButton[NUM_FACES];
+
+    for(int i=0; i<NUM_FACES; i++)
+      {
+      final int ii = i;
+      int padding = (int)(3*scale + 0.5f);
+      LinearLayout.LayoutParams objectParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT, 1.0f);
+      mColorButton[i] = new ImageButton(act);
+      mColorButton[i].setLayoutParams(objectParams);
+      mColorButton[i].setPadding(padding,0,padding,0);
+      mColorButton[i].setImageBitmap(mBitmap[i]);
+
+      mColorButton[i].setOnClickListener( new View.OnClickListener()
+        {
+        @Override
+        public void onClick(View view)
+          {
+          android.util.Log.e("solver", "button "+FACE_COLORS[ii]+" clicked");
+          }
+        });
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setupBackButton(final RubikActivity act, final float scale)
+    {
+    int padding = (int)(3*scale + 0.5f);
+    LinearLayout.LayoutParams backParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
+    mBackButton = new Button(act);
+    mBackButton.setLayoutParams(backParams);
+    mBackButton.setPadding(padding,0,padding,0);
+    mBackButton.setText(R.string.back);
+
+    mBackButton.setOnClickListener( new View.OnClickListener()
+      {
+      @Override
+      public void onClick(View v)
+        {
+        RubikState.goBack(act);
+        }
+      });
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void savePreferences(SharedPreferences.Editor editor)
+    {
+    mColorButton = null;
+    mBackButton  = null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void restorePreferences(SharedPreferences preferences)
+    {
+
+    }
+  }
diff --git a/src/main/java/org/distorted/uistate/RubikStateSolving.java b/src/main/java/org/distorted/uistate/RubikStateSolving.java
index db1468b6..79b80a81 100644
--- a/src/main/java/org/distorted/uistate/RubikStateSolving.java
+++ b/src/main/java/org/distorted/uistate/RubikStateSolving.java
@@ -67,7 +67,7 @@ public class RubikStateSolving extends RubikStateAbstract
     LayoutInflater inflater = act.getLayoutInflater();
 
     // TOP ////////////////////////////
-    LinearLayout layoutTop = act.findViewById(R.id.mainTitle);
+    LinearLayout layoutTop = act.findViewById(R.id.upperBar);
     layoutTop.removeAllViews();
     mTime = (TextView)inflater.inflate(R.layout.upper_text, null);
     mTime.setText(R.string.ready);
diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml
index 00cc9c17..f788bd59 100644
--- a/src/main/res/layout/main.xml
+++ b/src/main/res/layout/main.xml
@@ -5,7 +5,7 @@
     android:orientation="vertical" >
 
     <LinearLayout
-        android:id="@+id/mainTitle"
+        android:id="@+id/upperBar"
         android:layout_width="fill_parent"
         android:layout_height="50dp"
         android:gravity="center"
