Project

General

Profile

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

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

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.effect.VertexEffectSink;
32
import org.distorted.library.main.DistortedEffects;
33
import org.distorted.library.main.DistortedTexture;
34
import org.distorted.library.mesh.MeshBase;
35
import org.distorted.library.mesh.MeshJoined;
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[] loc;
200

    
201
    switch(size)
202
      {
203
      case 5 : loc = new float[] { -0.5f, 0.05f, 0.1f, 0.35f, 0.35f, 0.1f, 0.05f };
204
               break;
205
      case 2 : loc = new float[] { -0.5f, 0.03f, 0.05f, 0.07f, 0.20f, 0.30f, 0.20f, 0.07f, 0.05f, 0.03f };
206
               break;
207
      default: loc = new float[] { -0.5f, 0.04f, 0.06f, 0.25f, 0.30f, 0.25f, 0.06f, 0.04f };
208
      }
209

    
210
    return createCubitMesh(index,loc);
211
    }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

    
215
  MeshBase createCubitMesh(int index, float[] loc)
216
    {
217
    if( mMeshes==null )
218
      {
219
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
220
      }
221

    
222
    if( mMeshes[index]==null )
223
      {
224
      final int MESHES=6;
225
      int association = 1;
226
      MeshBase[] meshes = new MeshSquare[MESHES];
227
      meshes[0] = new MeshSquare(loc,loc);
228
      meshes[0].setEffectAssociation(0,association,0);
229

    
230
      for(int i=1; i<MESHES; i++)
231
        {
232
        association <<=1;
233
        meshes[i] = meshes[0].copy(true);
234
        meshes[i].setEffectAssociation(0,association,0);
235
        }
236

    
237
      mMeshes[index] = new MeshJoined(meshes);
238

    
239
      Static3D axisY   = new Static3D(0,1,0);
240
      Static3D axisX   = new Static3D(1,0,0);
241
      Static3D center  = new Static3D(0,0,0);
242
      Static1D angle90 = new Static1D(90);
243
      Static1D angle180= new Static1D(180);
244
      Static1D angle270= new Static1D(270);
245

    
246
      float d1 = 1.0f;
247
      float d2 =-0.05f;
248
      float d3 = 0.12f;
249

    
250
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
251
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
252
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
253
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
254
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
255
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
256
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
257
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
258

    
259
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
260
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
261
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
262
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
263
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
264
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
265
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
266
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
267

    
268
      Static4D dReg  = new Static4D(0,0,0,d3);
269
      Static1D dRad  = new Static1D(1);
270

    
271
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
272
      effect0.setMeshAssociation(63,-1);  // all 6 sides
273
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
274
      effect1.setMeshAssociation(32,-1);  // back
275
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
276
      effect2.setMeshAssociation( 8,-1);  // bottom
277
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
278
      effect3.setMeshAssociation( 4,-1);  // top
279
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
280
      effect4.setMeshAssociation( 2,-1);  // left
281
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
282
      effect5.setMeshAssociation( 1,-1);  // right
283

    
284
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
285
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
286
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
287
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
288
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
289
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
290
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
291
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
292

    
293
      VertexEffectSink   effect14= new VertexEffectSink( new Static1D(1.5f), center, new Static4D(0,0,0,0.72f) );
294

    
295
      mMeshes[index].apply(effect0);
296
      mMeshes[index].apply(effect1);
297
      mMeshes[index].apply(effect2);
298
      mMeshes[index].apply(effect3);
299
      mMeshes[index].apply(effect4);
300
      mMeshes[index].apply(effect5);
301
      mMeshes[index].apply(effect6);
302
      mMeshes[index].apply(effect7);
303
      mMeshes[index].apply(effect8);
304
      mMeshes[index].apply(effect9);
305
      mMeshes[index].apply(effect10);
306
      mMeshes[index].apply(effect11);
307
      mMeshes[index].apply(effect12);
308
      mMeshes[index].apply(effect13);
309
      mMeshes[index].apply(effect14);
310

    
311
      mMeshes[index].mergeEffComponents();
312
      }
313

    
314
    return mMeshes[index].copy(true);
315
    }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318

    
319
  float returnMultiplier()
320
    {
321
    return getSize();
322
    }
323

    
324
///////////////////////////////////////////////////////////////////////////////////////////////////
325

    
326
  float[] getRowChances()
327
    {
328
    int size = getSize();
329
    float[] chances = new float[size];
330

    
331
    for(int i=0; i<size; i++)
332
      {
333
      chances[i] = (i+1.0f) / size;
334
      }
335

    
336
    return chances;
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340
// PUBLIC API
341

    
342
  public Static3D[] getRotationAxis()
343
    {
344
    return ROT_AXIS;
345
    }
346

    
347
///////////////////////////////////////////////////////////////////////////////////////////////////
348

    
349
  public int getBasicAngle()
350
    {
351
    return 4;
352
    }
353

    
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355

    
356
  public int computeRowFromOffset(float offset)
357
    {
358
    return (int)(getSize()*offset);
359
    }
360

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362

    
363
  public float returnRotationFactor(float offset)
364
    {
365
    return 1.0f;
366
    }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

    
370
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
371
    {
372
    int numAxis = ROTATION_AXIS.length;
373

    
374
    if( oldRotAxis == START_AXIS )
375
      {
376
      return rnd.nextInt(numAxis);
377
      }
378
    else
379
      {
380
      int newVector = rnd.nextInt(numAxis-1);
381
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
382
      }
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386

    
387
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
388
    {
389
    float rowFloat = rnd.nextFloat();
390

    
391
    for(int row=0; row<mRowChances.length; row++)
392
      {
393
      if( rowFloat<=mRowChances[row] ) return row;
394
      }
395

    
396
    return 0;
397
    }
398

    
399
///////////////////////////////////////////////////////////////////////////////////////////////////
400

    
401
  public boolean isSolved()
402
    {
403
    int index = CUBITS[0].mQuatIndex;
404

    
405
    for(int i=1; i<NUM_CUBITS; i++)
406
      {
407
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
408
      }
409

    
410
    return true;
411
    }
412

    
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
415
// then if it were rotated by quaternion 'quat'.
416
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
417
// middle squares get interchanged. No visible difference!
418
//
419
// So: this is true iff the cubit
420
// a) is a corner or edge and the quaternions are the same
421
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
422

    
423
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
424
    {
425
    if ( cubit.mQuatIndex == quatIndex ) return true;
426

    
427
    int belongsToHowManyFaces = 0;
428
    int size = getSize()-1;
429
    float row;
430
    final float MAX_ERROR = 0.01f;
431

    
432
    for(int i=0; i<NUM_AXIS; i++)
433
      {
434
      row = cubit.mRotationRow[i];
435
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
436
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
437
      }
438

    
439
    switch(belongsToHowManyFaces)
440
      {
441
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
442
      case 1 :                // cubit that lies inside one of the faces
443
               Static3D orig = cubit.getOrigPosition();
444
               Static4D quat1 = QUATS[quatIndex];
445
               Static4D quat2 = QUATS[cubit.mQuatIndex];
446

    
447
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
448
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
449
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
450

    
451
               float row1, row2, row3, row4;
452
               float ax,ay,az;
453
               Static3D axis;
454
               float x1 = rotated1.get0();
455
               float y1 = rotated1.get1();
456
               float z1 = rotated1.get2();
457
               float x2 = rotated2.get0();
458
               float y2 = rotated2.get1();
459
               float z2 = rotated2.get2();
460

    
461
               for(int i=0; i<NUM_AXIS; i++)
462
                 {
463
                 axis = ROTATION_AXIS[i];
464
                 ax = axis.get0();
465
                 ay = axis.get1();
466
                 az = axis.get2();
467

    
468
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
469
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
470
                 row3 = row1 - size;
471
                 row4 = row2 - size;
472

    
473
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
474
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
475
                   {
476
                   return true;
477
                   }
478
                 }
479
               return false;
480

    
481
      default: return false;  // edge or corner
482
      }
483
    }
484

    
485
///////////////////////////////////////////////////////////////////////////////////////////////////
486
// order: Up --> Right --> Front --> Down --> Left --> Back
487
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
488
//
489
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
490
//
491
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
492
//
493
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
494
// Right :   index --> 6*s*s - 12*s + 7 - index
495
// Front :   index --> if b==0  : s*s - 1 - index
496
//                     if b==s-1: 6*s*s -11*s +6 - index
497
//                     else
498
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
499
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
500
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
501
// Left  :   index --> (s-1-a)*s + b
502
// Back  :   index --> if b==s-1: s*(s-1-a)
503
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
504
//                     else
505
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
506
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
507

    
508
  public String retObjectString()
509
    {
510
    StringBuilder objectString = new StringBuilder();
511
    int size = getSize();
512
    int len = size*size;
513
    int cubitIndex=-1, row=-1, col=-1;
514
    int color=-1, face=-1;
515

    
516
    final int RIGHT= 0;
517
    final int LEFT = 1;
518
    final int UP   = 2;
519
    final int DOWN = 3;
520
    final int FRONT= 4;
521
    final int BACK = 5;
522

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

    
525
/////////////////////
526
// LIVE DEBUGGING
527
/////////////////////
528
try
529
  {
530
/////////////////////
531

    
532
    face = UP;
533

    
534
    for(int i=0; i<len; i++)
535
      {
536
      row = i/size;
537
      col = i%size;
538

    
539
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
540
      color = getCubitFaceColorIndex(cubitIndex,face);
541
      objectString.append(FACE_NAMES[color]);
542
      }
543

    
544
    face = RIGHT;
545

    
546
    for(int i=0; i<len; i++)
547
      {
548
      cubitIndex = 6*size*size - 12*size +7 - i;
549
      color = getCubitFaceColorIndex(cubitIndex,face);
550
      objectString.append(FACE_NAMES[color]);
551
      }
552

    
553
     face = FRONT;
554

    
555
    for(int i=0; i<len; i++)
556
      {
557
      row = i/size;
558
      col = i%size;
559

    
560
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
561
      else if( col==0 ) cubitIndex = size*size - 1 - i;
562
      else
563
        {
564
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
565
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
566
        }
567

    
568
      color = getCubitFaceColorIndex(cubitIndex,face);
569
      objectString.append(FACE_NAMES[color]);
570
      }
571

    
572
    face = DOWN;
573

    
574
    for(int i=0; i<len; i++)
575
      {
576
      row = i/size;
577
      col = i%size;
578

    
579
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
580
      color = getCubitFaceColorIndex(cubitIndex,face);
581
      objectString.append(FACE_NAMES[color]);
582
      }
583

    
584
    face = LEFT;
585

    
586
    for(int i=0; i<len; i++)
587
      {
588
      row = i/size;
589
      col = i%size;
590

    
591
      cubitIndex = (size-1-row)*size + col;
592
      color = getCubitFaceColorIndex(cubitIndex,face);
593
      objectString.append(FACE_NAMES[color]);
594
      }
595

    
596
    face = BACK;
597

    
598
    for(int i=0; i<len; i++)
599
      {
600
      row = i/size;
601
      col = i%size;
602

    
603
      if( col==size-1 ) cubitIndex = size*(size-1-row);
604
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
605
      else
606
        {
607
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
608
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
609
        }
610

    
611
      color = getCubitFaceColorIndex(cubitIndex,face);
612
      objectString.append(FACE_NAMES[color]);
613
      }
614

    
615
/////////////////////
616
  }
617
catch(java.lang.ArrayIndexOutOfBoundsException ex)
618
  {
619
  FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
620

    
621
  String str="";
622
  for(int i=0; i<NUM_CUBITS; i++)
623
    {
624
    str += (CUBITS[i].mQuatIndex+" ");
625
    }
626

    
627
  crashlytics.setCustomKey("ObjectString", "color="+color+" cubitIndex="+cubitIndex+" face="+face+" row="+row+" col="+col );
628
  crashlytics.setCustomKey("Quaternion", "NUM_CUBITS: "+NUM_CUBITS+" quats: "+str );
629
  crashlytics.recordException(ex);
630
  }
631
/////////////////////
632
// END LIVE DEBUGGING
633
/////////////////////
634

    
635
    return objectString.toString();
636
    }
637
}
(2-2/10)