Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyCube.java @ ee39837d

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 org.distorted.library.effect.VertexEffectDeform;
27
import org.distorted.library.effect.VertexEffectMove;
28
import org.distorted.library.effect.VertexEffectRotate;
29
import org.distorted.library.main.DistortedEffects;
30
import org.distorted.library.main.DistortedTexture;
31
import org.distorted.library.mesh.MeshBase;
32
import org.distorted.library.mesh.MeshJoined;
33
import org.distorted.library.mesh.MeshPolygon;
34
import org.distorted.library.mesh.MeshSquare;
35
import org.distorted.library.type.Static1D;
36
import org.distorted.library.type.Static3D;
37
import org.distorted.library.type.Static4D;
38
import org.distorted.main.RubikSurfaceView;
39

    
40
import java.util.Random;
41

    
42
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

    
46
class TwistyCube extends TwistyObject
47
{
48
  static final float SQ2 = (float)Math.sqrt(2);
49

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

    
58
  private static final int[] FACE_COLORS = new int[]
59
         {
60
           COLOR_YELLOW, COLOR_WHITE,
61
           COLOR_BLUE  , COLOR_GREEN,
62
           COLOR_RED   , COLOR_BROWN
63
         };
64

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

    
79
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
80
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
81
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
82
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
83
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
84
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
85
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
86
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
87
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
88
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
89
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
90
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
91

    
92
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
93
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
94
         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
95
         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
96
         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
97
         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
98
         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
99
         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
100
         };
101

    
102
  private static MeshBase[] mMeshes;
103

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

    
106
  TwistyCube(int size, Static4D quat, DistortedTexture texture,
107
             MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
108
    {
109
    super(size, 60, quat, texture, mesh, effects, moves, ObjectList.CUBE, res, scrWidth);
110
    }
111

    
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113
// paint the square with upper-right corner at (left,top) and side length 'side' with texture
114
// for face 'face'.
115

    
116
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
117
    {
118
    final float R = side*0.10f;
119
    final float M = side*0.05f;
120

    
121
    paint.setColor(FACE_COLORS[face]);
122
    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
123
    }
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  Static3D[] getCubitPositions(int size)
128
    {
129
    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
130
    Static3D[] tmp = new Static3D[numCubits];
131

    
132
    float diff = 0.5f*(size-1);
133
    int currentPosition = 0;
134

    
135
    for(int x = 0; x<size; x++)
136
      for(int y = 0; y<size; y++)
137
        for(int z = 0; z<size; z++)
138
          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
139
            {
140
            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
141
            }
142

    
143
    return tmp;
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147

    
148
  Static4D[] getQuats()
149
    {
150
    return QUATS;
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154

    
155
  boolean shouldResetTextureMaps()
156
    {
157
    return false;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  int getNumFaces()
163
    {
164
    return FACE_COLORS.length;
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

    
169
  float getBasicStep()
170
    {
171
    return 1.0f;
172
    }
173

    
174
///////////////////////////////////////////////////////////////////////////////////////////////////
175

    
176
  int getNumStickerTypes()
177
    {
178
    return 1;
179
    }
180

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

    
183
  int getNumCubitFaces()
184
    {
185
    return FACE_COLORS.length;
186
    }
187

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189

    
190
  float getScreenRatio()
191
    {
192
    return 0.5f;
193
    }
194

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196

    
197
  int getFaceColor(int cubit, int cubitface, int size)
198
    {
199
    boolean belongs = isOnFace(cubit, cubitface/2, cubitface%2==0 ? size-1:0 );
200
    return belongs ? cubitface : NUM_FACES;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
  MeshBase createCubitMesh(int cubit)
206
    {
207
    int size   = getSize();
208
    int ordinal= ObjectList.CUBE.ordinal();
209
    int index  = ObjectList.getSizeIndex(ordinal,size);
210
    float[] bands;
211
    float D = 0.027f;
212
    float E = 0.5f-D;
213
    float[] vertices = { -E,-E, +E,-E, +E,+E, -E,+E };
214
    int extraI, extraV;
215

    
216
    switch(size)
217
      {
218
      case 2 : bands = new float[] { 1.0f    ,-D,
219
                                     1.0f-D/2,-D*0.55f,
220
                                     1.0f-D  ,-D*0.25f,
221
                                     1.0f-2*D, 0.0f,
222
                                     0.50f, 0.040f,
223
                                     0.0f, 0.048f };
224
               extraI = 2;
225
               extraV = 2;
226
               break;
227
      case 3 : bands = new float[] { 1.0f    ,-D,
228
                                     1.0f-D*1.2f,-D*0.55f,
229
                                     1.0f-2*D, 0.0f,
230
                                     0.50f, 0.040f,
231
                                     0.0f, 0.048f };
232
               extraI = 2;
233
               extraV = 2;
234
               break;
235
      case 4 : bands = new float[] { 1.0f    ,-D,
236
                                     1.0f-D*1.2f,-D*0.55f,
237
                                     1.0f-2*D, 0.0f,
238
                                     0.50f, 0.040f,
239
                                     0.0f, 0.048f };
240
               extraI = 1;
241
               extraV = 2;
242
               break;
243
      default: bands = new float[] { 1.0f    ,-D,
244
                                     1.0f-2*D, 0.0f,
245
                                     0.50f, 0.025f,
246
                                     0.0f, 0.030f };
247
               extraI = 1;
248
               extraV = 1;
249
               break;
250
      }
251

    
252
    return createCubitMesh(index,vertices,bands,extraI,extraV);
253
    }
254

    
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256

    
257
  MeshBase createCubitMesh(int index, float[] vertices, float[] bands, int extraI, int extraV)
258
    {
259
    if( mMeshes==null )
260
      {
261
      mMeshes = new MeshBase[ObjectList.CUBE.getNumVariants()];
262
      }
263

    
264
    if( mMeshes[index]==null )
265
      {
266
      final int MESHES=6;
267
      int association = 1;
268
      MeshBase[] meshes = new MeshPolygon[MESHES];
269
      meshes[0] = new MeshPolygon(vertices,bands,extraI,extraV);
270
      meshes[0].setEffectAssociation(0,association,0);
271

    
272
      for(int i=1; i<MESHES; i++)
273
        {
274
        association <<=1;
275
        meshes[i] = meshes[0].copy(true);
276
        meshes[i].setEffectAssociation(0,association,0);
277
        }
278

    
279
      mMeshes[index] = new MeshJoined(meshes);
280

    
281
      Static3D axisY   = new Static3D(0,1,0);
282
      Static3D axisX   = new Static3D(1,0,0);
283
      Static3D center  = new Static3D(0,0,0);
284
      Static1D angle90 = new Static1D(90);
285
      Static1D angle180= new Static1D(180);
286
      Static1D angle270= new Static1D(270);
287

    
288
      float d1 = 1.0f;
289
      float d2 =-0.05f;
290
      float d3 = 0.12f;
291

    
292
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
293
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
294
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
295
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
296
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
297
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
298
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
299
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
300

    
301
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
302
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
303
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
304
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
305
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
306
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
307
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
308
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
309

    
310
      Static4D dReg  = new Static4D(0,0,0,d3);
311
      Static1D dRad  = new Static1D(1);
312

    
313
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
314
      effect0.setMeshAssociation(63,-1);  // all 6 sides
315
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
316
      effect1.setMeshAssociation(32,-1);  // back
317
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
318
      effect2.setMeshAssociation( 8,-1);  // bottom
319
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
320
      effect3.setMeshAssociation( 4,-1);  // top
321
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
322
      effect4.setMeshAssociation( 2,-1);  // left
323
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
324
      effect5.setMeshAssociation( 1,-1);  // right
325

    
326
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
327
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
328
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
329
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
330
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
331
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
332
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
333
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
334

    
335
      mMeshes[index].apply(effect0);
336
      mMeshes[index].apply(effect1);
337
      mMeshes[index].apply(effect2);
338
      mMeshes[index].apply(effect3);
339
      mMeshes[index].apply(effect4);
340
      mMeshes[index].apply(effect5);
341
      mMeshes[index].apply(effect6);
342
      mMeshes[index].apply(effect7);
343
      mMeshes[index].apply(effect8);
344
      mMeshes[index].apply(effect9);
345
      mMeshes[index].apply(effect10);
346
      mMeshes[index].apply(effect11);
347
      mMeshes[index].apply(effect12);
348
      mMeshes[index].apply(effect13);
349

    
350
      mMeshes[index].mergeEffComponents();
351
      }
352

    
353
    return mMeshes[index].copy(true);
354
    }
355

    
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357

    
358
  float returnMultiplier()
359
    {
360
    return getSize();
361
    }
362

    
363
///////////////////////////////////////////////////////////////////////////////////////////////////
364

    
365
  float[] getRowChances()
366
    {
367
    int size = getSize();
368
    float[] chances = new float[size];
369

    
370
    for(int i=0; i<size; i++)
371
      {
372
      chances[i] = (i+1.0f) / size;
373
      }
374

    
375
    return chances;
376
    }
377

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
// PUBLIC API
380

    
381
  public Static3D[] getRotationAxis()
382
    {
383
    return ROT_AXIS;
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387

    
388
  public int getBasicAngle()
389
    {
390
    return 4;
391
    }
392

    
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394

    
395
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
396
    {
397
    int numAxis = ROTATION_AXIS.length;
398

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

    
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411

    
412
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
413
    {
414
    float rowFloat = rnd.nextFloat();
415

    
416
    for(int row=0; row<mRowChances.length; row++)
417
      {
418
      if( rowFloat<=mRowChances[row] ) return row;
419
      }
420

    
421
    return 0;
422
    }
423

    
424
///////////////////////////////////////////////////////////////////////////////////////////////////
425

    
426
  public boolean isSolved()
427
    {
428
    int index = CUBITS[0].mQuatIndex;
429

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

    
435
    return true;
436
    }
437

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

    
448
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
449
    {
450
    if ( cubit.mQuatIndex == quatIndex ) return true;
451

    
452
    int belongsToHowManyFaces = 0;
453
    int size = getSize()-1;
454
    float row;
455
    final float MAX_ERROR = 0.01f;
456

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

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

    
472
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
473
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
474
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
475

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

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

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

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

    
506
      default: return false;  // edge or corner
507
      }
508
    }
509

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

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

    
541
    final int RIGHT= 0;
542
    final int LEFT = 1;
543
    final int UP   = 2;
544
    final int DOWN = 3;
545
    final int FRONT= 4;
546
    final int BACK = 5;
547

    
548
    // 'I' - interior, theoretically can happen
549
    final char[] FACE_NAMES = { 'R', 'L', 'U', 'D', 'F', 'B', 'I'};
550

    
551
    face = UP;
552

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

    
558
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
559
      color = getCubitFaceColorIndex(cubitIndex,face);
560
      objectString.append(FACE_NAMES[color]);
561
      }
562

    
563
    face = RIGHT;
564

    
565
    for(int i=0; i<len; i++)
566
      {
567
      cubitIndex = 6*size*size - 12*size +7 - i;
568
      color = getCubitFaceColorIndex(cubitIndex,face);
569
      objectString.append(FACE_NAMES[color]);
570
      }
571

    
572
     face = FRONT;
573

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

    
579
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
580
      else if( col==0 ) cubitIndex = size*size - 1 - i;
581
      else
582
        {
583
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
584
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
585
        }
586

    
587
      color = getCubitFaceColorIndex(cubitIndex,face);
588
      objectString.append(FACE_NAMES[color]);
589
      }
590

    
591
    face = DOWN;
592

    
593
    for(int i=0; i<len; i++)
594
      {
595
      row = i/size;
596
      col = i%size;
597

    
598
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
599
      color = getCubitFaceColorIndex(cubitIndex,face);
600
      objectString.append(FACE_NAMES[color]);
601
      }
602

    
603
    face = LEFT;
604

    
605
    for(int i=0; i<len; i++)
606
      {
607
      row = i/size;
608
      col = i%size;
609

    
610
      cubitIndex = (size-1-row)*size + col;
611
      color = getCubitFaceColorIndex(cubitIndex,face);
612
      objectString.append(FACE_NAMES[color]);
613
      }
614

    
615
    face = BACK;
616

    
617
    for(int i=0; i<len; i++)
618
      {
619
      row = i/size;
620
      col = i%size;
621

    
622
      if( col==size-1 ) cubitIndex = size*(size-1-row);
623
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
624
      else
625
        {
626
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
627
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
628
        }
629

    
630
      color = getCubitFaceColorIndex(cubitIndex,face);
631
      objectString.append(FACE_NAMES[color]);
632
      }
633

    
634
    return objectString.toString();
635
    }
636
}
(10-10/18)