Project

General

Profile

Download (23.7 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / objects / RubikCube.java @ c0b37c89

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.objects;
21

    
22
import android.content.res.Resources;
23
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import com.google.firebase.crashlytics.FirebaseCrashlytics;
27

    
28
import org.distorted.library.effect.VertexEffectDeform;
29
import org.distorted.library.effect.VertexEffectMove;
30
import org.distorted.library.effect.VertexEffectRotate;
31
import org.distorted.library.main.DistortedEffects;
32
import org.distorted.library.main.DistortedTexture;
33
import org.distorted.library.mesh.MeshBase;
34
import org.distorted.library.mesh.MeshJoined;
35
import org.distorted.library.mesh.MeshPolygon;
36
import org.distorted.library.mesh.MeshSquare;
37
import org.distorted.library.type.Static1D;
38
import org.distorted.library.type.Static3D;
39
import org.distorted.library.type.Static4D;
40
import org.distorted.main.RubikSurfaceView;
41

    
42
import java.util.Random;
43

    
44
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
45

    
46
///////////////////////////////////////////////////////////////////////////////////////////////////
47

    
48
class RubikCube extends RubikObject
49
{
50
  static final float SQ2 = (float)Math.sqrt(2);
51

    
52
  // the three rotation axis of a RubikCube. Must be normalized.
53
  static final Static3D[] ROT_AXIS = new Static3D[]
54
         {
55
           new Static3D(1,0,0),
56
           new Static3D(0,1,0),
57
           new Static3D(0,0,1)
58
         };
59

    
60
  // the six axis that determine the faces
61
  static final Static3D[] FACE_AXIS = new Static3D[]
62
         {
63
           new Static3D(1,0,0), new Static3D(-1,0,0),
64
           new Static3D(0,1,0), new Static3D(0,-1,0),
65
           new Static3D(0,0,1), new Static3D(0,0,-1)
66
         };
67

    
68
  private static final int[] FACE_COLORS = new int[]
69
         {
70
           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
71
           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
72
           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
73
         };
74

    
75
  // All legal rotation quats of a RubikCube of any size.
76
  // Here's how to compute this:
77
  // 1) compute how many rotations there are (RubikCube of any size = 24)
78
  // 2) take the AXIS, angles of rotation (90 in RubikCube's case) compute the basic quaternions
79
  // (i.e. rotations of 1 basic angle along each of the axis) and from there start semi-randomly
80
  // multiplying them and eventually you'll find all (24) legal rotations.
81
  // Example program in C, res/raw/compute_quats.c , is included.
82
  private static final Static4D[] QUATS = new Static4D[]
83
         {
84
         new Static4D(  0.0f,   0.0f,   0.0f,   1.0f),
85
         new Static4D(  1.0f,   0.0f,   0.0f,   0.0f),
86
         new Static4D(  0.0f,   1.0f,   0.0f,   0.0f),
87
         new Static4D(  0.0f,   0.0f,   1.0f,   0.0f),
88

    
89
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
90
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
91
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
92
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
93
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
94
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
95
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
96
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
97
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
98
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
99
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
100
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
101

    
102
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
103
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
104
         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
105
         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
106
         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
107
         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
108
         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
109
         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
110
         };
111

    
112
  private static MeshBase[] mMeshes;
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
  RubikCube(int size, Static4D quat, DistortedTexture texture,
117
            MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
118
    {
119
    super(size, 60, quat, texture, mesh, effects, moves, RubikObjectList.CUBE, res, scrWidth);
120
    }
121

    
122
///////////////////////////////////////////////////////////////////////////////////////////////////
123
// paint the square with upper-right corner at (left,top) and side length 'side' with texture
124
// for face 'face'.
125

    
126
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
127
    {
128
    final float R = side*0.10f;
129
    final float M = side*0.05f;
130

    
131
    paint.setColor(FACE_COLORS[face]);
132
    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

    
137
  Static3D[] getCubitPositions(int size)
138
    {
139
    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
140
    Static3D[] tmp = new Static3D[numCubits];
141

    
142
    float diff = 0.5f*(size-1);
143
    int currentPosition = 0;
144

    
145
    for(int x = 0; x<size; x++)
146
      for(int y = 0; y<size; y++)
147
        for(int z = 0; z<size; z++)
148
          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
149
            {
150
            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
151
            }
152

    
153
    return tmp;
154
    }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157

    
158
  Static4D[] getQuats()
159
    {
160
    return QUATS;
161
    }
162

    
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164

    
165
  int getNumFaces()
166
    {
167
    return FACE_COLORS.length;
168
    }
169

    
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171

    
172
  int getNumCubitFaces()
173
    {
174
    return FACE_COLORS.length;
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

    
179
  float getScreenRatio()
180
    {
181
    return 0.5f;
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

    
186
  int getFaceColor(int cubit, int cubitface, int size)
187
    {
188
    boolean belongs = isOnFace(cubit, cubitface/2, cubitface%2==0 ? size-1:0 );
189
    return belongs ? cubitface : NUM_FACES;
190
    }
191

    
192
///////////////////////////////////////////////////////////////////////////////////////////////////
193

    
194
  MeshBase createCubitMesh(int cubit)
195
    {
196
    int size   = getSize();
197
    int ordinal= RubikObjectList.CUBE.ordinal();
198
    int index  = RubikObjectList.getSizeIndex(ordinal,size);
199
    float[] bands;
200
    float D = 0.027f;
201
    float E = 0.5f-D;
202
    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
203
    int extraI, extraV;
204

    
205
    switch(size)
206
      {
207
      case 2 : bands = new float[] { 1.0f    ,-D,
208
                                     1.0f-D/2,-D*0.45f,
209
                                     1.0f-D  ,-D*0.80f,
210
                                     1.0f-2*D, 0.0f,
211
                                     0.50f, 0.040f,
212
                                     0.0f, 0.048f };
213
               extraI = 2;
214
               extraV = 2;
215
               break;
216
      case 3 : bands = new float[] { 1.0f    ,-D,
217
                                     1.0f-D*1.2f,-D*0.55f,
218
                                     1.0f-2*D, 0.0f,
219
                                     0.50f, 0.040f,
220
                                     0.0f, 0.048f };
221
               extraI = 2;
222
               extraV = 2;
223
               break;
224
      case 4 : bands = new float[] { 1.0f    ,-D,
225
                                     1.0f-D*1.2f,-D*0.55f,
226
                                     1.0f-2*D, 0.0f,
227
                                     0.50f, 0.040f,
228
                                     0.0f, 0.048f };
229
               extraI = 1;
230
               extraV = 2;
231
               break;
232
      default: bands = new float[] { 1.0f    ,-D,
233
                                     1.0f-2*D, 0.0f,
234
                                     0.50f, 0.025f,
235
                                     0.0f, 0.030f };
236
               extraI = 1;
237
               extraV = 1;
238
               break;
239
      }
240

    
241
    return createCubitMesh(index,vertices,bands,extraI,extraV);
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  MeshBase createCubitMesh(int index, float[] vertices, float[] bands, int extraI, int extraV)
247
    {
248
    if( mMeshes==null )
249
      {
250
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
251
      }
252

    
253
    if( mMeshes[index]==null )
254
      {
255
      final int MESHES=6;
256
      int association = 1;
257
      MeshBase[] meshes = new MeshPolygon[MESHES];
258
      meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
259
      meshes[0].setEffectAssociation(0,association,0);
260

    
261
      for(int i=1; i<MESHES; i++)
262
        {
263
        association <<=1;
264
        meshes[i] = meshes[0].copy(true);
265
        meshes[i].setEffectAssociation(0,association,0);
266
        }
267

    
268
      mMeshes[index] = new MeshJoined(meshes);
269

    
270
      Static3D axisY   = new Static3D(0,1,0);
271
      Static3D axisX   = new Static3D(1,0,0);
272
      Static3D center  = new Static3D(0,0,0);
273
      Static1D angle90 = new Static1D(90);
274
      Static1D angle180= new Static1D(180);
275
      Static1D angle270= new Static1D(270);
276

    
277
      float d1 = 1.0f;
278
      float d2 =-0.05f;
279
      float d3 = 0.12f;
280

    
281
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
282
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
283
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
284
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
285
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
286
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
287
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
288
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
289

    
290
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
291
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
292
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
293
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
294
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
295
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
296
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
297
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
298

    
299
      Static4D dReg  = new Static4D(0,0,0,d3);
300
      Static1D dRad  = new Static1D(1);
301

    
302
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
303
      effect0.setMeshAssociation(63,-1);  // all 6 sides
304
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
305
      effect1.setMeshAssociation(32,-1);  // back
306
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
307
      effect2.setMeshAssociation( 8,-1);  // bottom
308
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
309
      effect3.setMeshAssociation( 4,-1);  // top
310
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
311
      effect4.setMeshAssociation( 2,-1);  // left
312
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
313
      effect5.setMeshAssociation( 1,-1);  // right
314

    
315
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
316
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
317
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
318
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
319
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
320
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
321
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
322
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
323

    
324
      mMeshes[index].apply(effect0);
325
      mMeshes[index].apply(effect1);
326
      mMeshes[index].apply(effect2);
327
      mMeshes[index].apply(effect3);
328
      mMeshes[index].apply(effect4);
329
      mMeshes[index].apply(effect5);
330
      mMeshes[index].apply(effect6);
331
      mMeshes[index].apply(effect7);
332
      mMeshes[index].apply(effect8);
333
      mMeshes[index].apply(effect9);
334
      mMeshes[index].apply(effect10);
335
      mMeshes[index].apply(effect11);
336
      mMeshes[index].apply(effect12);
337
      mMeshes[index].apply(effect13);
338

    
339
      mMeshes[index].mergeEffComponents();
340
      }
341

    
342
    return mMeshes[index].copy(true);
343
    }
344

    
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346

    
347
  float returnMultiplier()
348
    {
349
    return getSize();
350
    }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

    
354
  float[] getRowChances()
355
    {
356
    int size = getSize();
357
    float[] chances = new float[size];
358

    
359
    for(int i=0; i<size; i++)
360
      {
361
      chances[i] = (i+1.0f) / size;
362
      }
363

    
364
    return chances;
365
    }
366

    
367
///////////////////////////////////////////////////////////////////////////////////////////////////
368
// PUBLIC API
369

    
370
  public Static3D[] getRotationAxis()
371
    {
372
    return ROT_AXIS;
373
    }
374

    
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376

    
377
  public int getBasicAngle()
378
    {
379
    return 4;
380
    }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
  public int computeRowFromOffset(float offset)
385
    {
386
    return (int)(getSize()*offset);
387
    }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390

    
391
  public float returnRotationFactor(float offset)
392
    {
393
    return 1.0f;
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
399
    {
400
    int numAxis = ROTATION_AXIS.length;
401

    
402
    if( oldRotAxis == START_AXIS )
403
      {
404
      return rnd.nextInt(numAxis);
405
      }
406
    else
407
      {
408
      int newVector = rnd.nextInt(numAxis-1);
409
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
410
      }
411
    }
412

    
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414

    
415
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
416
    {
417
    float rowFloat = rnd.nextFloat();
418

    
419
    for(int row=0; row<mRowChances.length; row++)
420
      {
421
      if( rowFloat<=mRowChances[row] ) return row;
422
      }
423

    
424
    return 0;
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428

    
429
  public boolean isSolved()
430
    {
431
    int index = CUBITS[0].mQuatIndex;
432

    
433
    for(int i=1; i<NUM_CUBITS; i++)
434
      {
435
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
436
      }
437

    
438
    return true;
439
    }
440

    
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
443
// then if it were rotated by quaternion 'quat'.
444
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
445
// middle squares get interchanged. No visible difference!
446
//
447
// So: this is true iff the cubit
448
// a) is a corner or edge and the quaternions are the same
449
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
450

    
451
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
452
    {
453
    if ( cubit.mQuatIndex == quatIndex ) return true;
454

    
455
    int belongsToHowManyFaces = 0;
456
    int size = getSize()-1;
457
    float row;
458
    final float MAX_ERROR = 0.01f;
459

    
460
    for(int i=0; i<NUM_AXIS; i++)
461
      {
462
      row = cubit.mRotationRow[i];
463
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
464
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
465
      }
466

    
467
    switch(belongsToHowManyFaces)
468
      {
469
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
470
      case 1 :                // cubit that lies inside one of the faces
471
               Static3D orig = cubit.getOrigPosition();
472
               Static4D quat1 = QUATS[quatIndex];
473
               Static4D quat2 = QUATS[cubit.mQuatIndex];
474

    
475
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
476
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
477
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
478

    
479
               float row1, row2, row3, row4;
480
               float ax,ay,az;
481
               Static3D axis;
482
               float x1 = rotated1.get0();
483
               float y1 = rotated1.get1();
484
               float z1 = rotated1.get2();
485
               float x2 = rotated2.get0();
486
               float y2 = rotated2.get1();
487
               float z2 = rotated2.get2();
488

    
489
               for(int i=0; i<NUM_AXIS; i++)
490
                 {
491
                 axis = ROTATION_AXIS[i];
492
                 ax = axis.get0();
493
                 ay = axis.get1();
494
                 az = axis.get2();
495

    
496
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
497
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
498
                 row3 = row1 - size;
499
                 row4 = row2 - size;
500

    
501
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
502
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
503
                   {
504
                   return true;
505
                   }
506
                 }
507
               return false;
508

    
509
      default: return false;  // edge or corner
510
      }
511
    }
512

    
513
///////////////////////////////////////////////////////////////////////////////////////////////////
514
// order: Up --> Right --> Front --> Down --> Left --> Back
515
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
516
//
517
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
518
//
519
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
520
//
521
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
522
// Right :   index --> 6*s*s - 12*s + 7 - index
523
// Front :   index --> if b==0  : s*s - 1 - index
524
//                     if b==s-1: 6*s*s -11*s +6 - index
525
//                     else
526
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
527
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
528
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
529
// Left  :   index --> (s-1-a)*s + b
530
// Back  :   index --> if b==s-1: s*(s-1-a)
531
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
532
//                     else
533
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
534
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
535

    
536
  public String retObjectString()
537
    {
538
    StringBuilder objectString = new StringBuilder();
539
    int size = getSize();
540
    int len = size*size;
541
    int cubitIndex=-1, row=-1, col=-1;
542
    int color=-1, face=-1;
543

    
544
    final int RIGHT= 0;
545
    final int LEFT = 1;
546
    final int UP   = 2;
547
    final int DOWN = 3;
548
    final int FRONT= 4;
549
    final int BACK = 5;
550

    
551
    final char[] FACE_NAMES = { 'R', 'L', 'U', 'D', 'F', 'B'};
552

    
553
/////////////////////
554
// LIVE DEBUGGING
555
/////////////////////
556
try
557
  {
558
/////////////////////
559

    
560
    face = UP;
561

    
562
    for(int i=0; i<len; i++)
563
      {
564
      row = i/size;
565
      col = i%size;
566

    
567
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
568
      color = getCubitFaceColorIndex(cubitIndex,face);
569
      objectString.append(FACE_NAMES[color]);
570
      }
571

    
572
    face = RIGHT;
573

    
574
    for(int i=0; i<len; i++)
575
      {
576
      cubitIndex = 6*size*size - 12*size +7 - i;
577
      color = getCubitFaceColorIndex(cubitIndex,face);
578
      objectString.append(FACE_NAMES[color]);
579
      }
580

    
581
     face = FRONT;
582

    
583
    for(int i=0; i<len; i++)
584
      {
585
      row = i/size;
586
      col = i%size;
587

    
588
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
589
      else if( col==0 ) cubitIndex = size*size - 1 - i;
590
      else
591
        {
592
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
593
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
594
        }
595

    
596
      color = getCubitFaceColorIndex(cubitIndex,face);
597
      objectString.append(FACE_NAMES[color]);
598
      }
599

    
600
    face = DOWN;
601

    
602
    for(int i=0; i<len; i++)
603
      {
604
      row = i/size;
605
      col = i%size;
606

    
607
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
608
      color = getCubitFaceColorIndex(cubitIndex,face);
609
      objectString.append(FACE_NAMES[color]);
610
      }
611

    
612
    face = LEFT;
613

    
614
    for(int i=0; i<len; i++)
615
      {
616
      row = i/size;
617
      col = i%size;
618

    
619
      cubitIndex = (size-1-row)*size + col;
620
      color = getCubitFaceColorIndex(cubitIndex,face);
621
      objectString.append(FACE_NAMES[color]);
622
      }
623

    
624
    face = BACK;
625

    
626
    for(int i=0; i<len; i++)
627
      {
628
      row = i/size;
629
      col = i%size;
630

    
631
      if( col==size-1 ) cubitIndex = size*(size-1-row);
632
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
633
      else
634
        {
635
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
636
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
637
        }
638

    
639
      color = getCubitFaceColorIndex(cubitIndex,face);
640
      objectString.append(FACE_NAMES[color]);
641
      }
642

    
643
/////////////////////
644
  }
645
catch(java.lang.ArrayIndexOutOfBoundsException ex)
646
  {
647
  FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
648

    
649
  String str="";
650
  for(int i=0; i<NUM_CUBITS; i++)
651
    {
652
    str += (CUBITS[i].mQuatIndex+" ");
653
    }
654

    
655
  crashlytics.setCustomKey("ObjectString", "color="+color+" cubitIndex="+cubitIndex+" face="+face+" row="+row+" col="+col );
656
  crashlytics.setCustomKey("Quaternion", "NUM_CUBITS: "+NUM_CUBITS+" quats: "+str );
657
  crashlytics.recordException(ex);
658
  }
659
/////////////////////
660
// END LIVE DEBUGGING
661
/////////////////////
662

    
663
    return objectString.toString();
664
    }
665
}
(2-2/10)