Project

General

Profile

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

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

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.effect.VertexEffectSink;
30
import org.distorted.library.main.DistortedEffects;
31
import org.distorted.library.main.DistortedTexture;
32
import org.distorted.library.mesh.MeshBase;
33
import org.distorted.library.mesh.MeshJoined;
34
import org.distorted.library.mesh.MeshRectangles;
35
import org.distorted.library.type.Static1D;
36
import org.distorted.library.type.Static3D;
37
import org.distorted.library.type.Static4D;
38

    
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40

    
41
class RubikCube extends RubikObject
42
{
43
  static final float SQ2 = (float)Math.sqrt(2);
44

    
45
  // the three rotation axis of a RubikCube. Must be normalized.
46
  static final Static3D[] ROT_AXIS = new Static3D[]
47
         {
48
           new Static3D(1,0,0),
49
           new Static3D(0,1,0),
50
           new Static3D(0,0,1)
51
         };
52

    
53
  // the six axis that determine the faces
54
  static final Static3D[] FACE_AXIS = new Static3D[]
55
         {
56
           new Static3D(1,0,0), new Static3D(-1,0,0),
57
           new Static3D(0,1,0), new Static3D(0,-1,0),
58
           new Static3D(0,0,1), new Static3D(0,0,-1)
59
         };
60

    
61
  private static final int[] FACE_COLORS = new int[]
62
         {
63
           0xffffff00, 0xffffffff,   // FACE_AXIS[0] (right-YELLOW) FACE_AXIS[1] (left  -WHITE)
64
           0xff0000ff, 0xff00ff00,   // FACE_AXIS[2] (top  -BLUE  ) FACE_AXIS[3] (bottom-GREEN)
65
           0xffff0000, 0xffb5651d    // FACE_AXIS[4] (front-RED   ) FACE_AXIS[5] (back  -BROWN)
66
         };
67

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

    
82
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
83
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
84
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
85
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
86
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
87
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
88
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
89
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
90
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
91
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
92
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
93
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
94

    
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
         new Static4D( -0.5f,   0.5f,  -0.5f,  -0.5f),
101
         new Static4D( -0.5f,   0.5f,   0.5f,  -0.5f),
102
         new Static4D( -0.5f,   0.5f,   0.5f,   0.5f)
103
         };
104

    
105
  private static MeshBase[] mMeshes;
106

    
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108

    
109
  RubikCube(int size, Static4D quat, DistortedTexture texture,
110
            MeshRectangles mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
111
    {
112
    super(size, 60, quat, texture, mesh, effects, moves, RubikObjectList.CUBE, res, scrWidth);
113
    }
114

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

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

    
124
    paint.setColor(FACE_COLORS[face]);
125
    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
126
    }
127

    
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129

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

    
135
    float diff = 0.5f*(size-1);
136
    int currentPosition = 0;
137

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

    
146
    return tmp;
147
    }
148

    
149
///////////////////////////////////////////////////////////////////////////////////////////////////
150

    
151
  Static4D[] getQuats()
152
    {
153
    return QUATS;
154
    }
155

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

    
158
  int getNumFaces()
159
    {
160
    return FACE_COLORS.length;
161
    }
162

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

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

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

    
172
  float getScreenRatio()
173
    {
174
    return 0.5f;
175
    }
176

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

    
179
  int getFaceColor(int cubit, int cubitface, int size)
180
    {
181
    boolean belongs = isOnFace(cubit, cubitface/2, cubitface%2==0 ? size-1:0 );
182
    return belongs ? cubitface : NUM_FACES;
183
    }
184

    
185
///////////////////////////////////////////////////////////////////////////////////////////////////
186

    
187
  MeshBase createCubitMesh(int cubit)
188
    {
189
    int size   = getSize();
190
    int ordinal= RubikObjectList.CUBE.ordinal();
191
    int index  = RubikObjectList.getSizeIndex(ordinal,size);
192
    float[] loc;
193

    
194
    switch(size)
195
      {
196
      case 5 : loc = new float[] { -0.5f, 0.05f, 0.1f, 0.35f, 0.35f, 0.1f, 0.05f };
197
               break;
198
      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 };
199
               break;
200
      default: loc = new float[] { -0.5f, 0.04f, 0.06f, 0.25f, 0.30f, 0.25f, 0.06f, 0.04f };
201
      }
202

    
203
    return createCubitMesh(index,loc);
204
    }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207

    
208
  MeshBase createCubitMesh(int index, float[] loc)
209
    {
210
    if( mMeshes==null )
211
      {
212
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
213
      }
214

    
215
    if( mMeshes[index]==null )
216
      {
217
      final int MESHES=6;
218
      int association = 1;
219
      MeshBase[] meshes = new MeshRectangles[MESHES];
220
      meshes[0] = new MeshRectangles(loc,loc);
221
      meshes[0].setEffectAssociation(0,association,0);
222

    
223
      for(int i=1; i<MESHES; i++)
224
        {
225
        association <<=1;
226
        meshes[i] = meshes[0].copy(true);
227
        meshes[i].setEffectAssociation(0,association,0);
228
        }
229

    
230
      mMeshes[index] = new MeshJoined(meshes);
231

    
232
      Static3D axisY   = new Static3D(0,1,0);
233
      Static3D axisX   = new Static3D(1,0,0);
234
      Static3D center  = new Static3D(0,0,0);
235
      Static1D angle90 = new Static1D(90);
236
      Static1D angle180= new Static1D(180);
237
      Static1D angle270= new Static1D(270);
238

    
239
      float d1 = 1.0f;
240
      float d2 =-0.05f;
241
      float d3 = 0.12f;
242

    
243
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
244
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
245
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
246
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
247
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
248
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
249
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
250
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
251

    
252
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
253
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
254
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
255
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
256
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
257
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
258
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
259
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
260

    
261
      Static4D dReg  = new Static4D(0,0,0,d3);
262
      Static1D dRad  = new Static1D(1);
263

    
264
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
265
      effect0.setMeshAssociation(63,-1);  // all 6 sides
266
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
267
      effect1.setMeshAssociation(32,-1);  // back
268
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
269
      effect2.setMeshAssociation( 8,-1);  // bottom
270
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
271
      effect3.setMeshAssociation( 4,-1);  // top
272
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
273
      effect4.setMeshAssociation( 2,-1);  // left
274
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
275
      effect5.setMeshAssociation( 1,-1);  // right
276

    
277
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
278
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
279
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
280
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
281
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
282
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
283
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
284
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
285

    
286
      VertexEffectSink   effect14= new VertexEffectSink( new Static1D(1.5f), center, new Static4D(0,0,0,0.72f) );
287

    
288
      mMeshes[index].apply(effect0);
289
      mMeshes[index].apply(effect1);
290
      mMeshes[index].apply(effect2);
291
      mMeshes[index].apply(effect3);
292
      mMeshes[index].apply(effect4);
293
      mMeshes[index].apply(effect5);
294
      mMeshes[index].apply(effect6);
295
      mMeshes[index].apply(effect7);
296
      mMeshes[index].apply(effect8);
297
      mMeshes[index].apply(effect9);
298
      mMeshes[index].apply(effect10);
299
      mMeshes[index].apply(effect11);
300
      mMeshes[index].apply(effect12);
301
      mMeshes[index].apply(effect13);
302
      mMeshes[index].apply(effect14);
303

    
304
      mMeshes[index].mergeEffComponents();
305
      }
306

    
307
    return mMeshes[index].copy(true);
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

    
312
  float returnMultiplier()
313
    {
314
    return getSize();
315
    }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318
// PUBLIC API
319

    
320
  public Static3D[] getRotationAxis()
321
    {
322
    return ROT_AXIS;
323
    }
324

    
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

    
327
  public int getBasicAngle()
328
    {
329
    return 4;
330
    }
331

    
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333

    
334
  public int computeRowFromOffset(float offset)
335
    {
336
    return (int)(getSize()*offset);
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340

    
341
  public float[] getRowChances()
342
    {
343
    int size = getSize();
344
    float[] chances = new float[size];
345

    
346
    for(int i=0; i<size; i++)
347
      {
348
      chances[i] = (i+1.0f) / size;
349
      }
350

    
351
    return chances;
352
    }
353

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

    
356
  public float returnRotationFactor(float offset)
357
    {
358
    return 1.0f;
359
    }
360

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362
// order: Up --> Right --> Front --> Down --> Left --> Back
363
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
364
//
365
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
366
//
367
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
368
//
369
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
370
// Right :   index --> 6*s*s - 12*s + 7 - index
371
// Front :   index --> if b==0  : s*s - 1 - index
372
//                     if b==s-1: 6*s*s -11*s +6 - index
373
//                     else
374
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
375
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
376
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
377
// Left  :   index --> (s-1-a)*s + b
378
// Back  :   index --> if b==s-1: s*(s-1-a)
379
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
380
//                     else
381
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
382
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
383

    
384
  public String retObjectString()
385
    {
386
    StringBuilder objectString = new StringBuilder();
387
    int size = getSize();
388
    int len = size*size;
389
    int cubitIndex, row, col;
390
    int color;
391

    
392
    final int RIGHT= 0;
393
    final int LEFT = 1;
394
    final int UP   = 2;
395
    final int DOWN = 3;
396
    final int FRONT= 4;
397
    final int BACK = 5;
398

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

    
401
    for(int i=0; i<len; i++)
402
      {
403
      row = i/size;
404
      col = i%size;
405

    
406
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
407
      color = getCubitFaceColorIndex(cubitIndex,UP);
408
      objectString.append(FACE_NAMES[color]);
409
      }
410

    
411
    for(int i=0; i<len; i++)
412
      {
413
      cubitIndex = 6*size*size - 12*size +7 - i;
414
      color = getCubitFaceColorIndex(cubitIndex,RIGHT);
415
      objectString.append(FACE_NAMES[color]);
416
      }
417

    
418
    for(int i=0; i<len; i++)
419
      {
420
      row = i/size;
421
      col = i%size;
422

    
423
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
424
      else if( col==0 ) cubitIndex = size*size - 1 - i;
425
      else
426
        {
427
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
428
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
429
        }
430

    
431
      color = getCubitFaceColorIndex(cubitIndex,FRONT);
432
      objectString.append(FACE_NAMES[color]);
433
      }
434

    
435
    for(int i=0; i<len; i++)
436
      {
437
      row = i/size;
438
      col = i%size;
439

    
440
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
441
      color = getCubitFaceColorIndex(cubitIndex,DOWN);
442
      objectString.append(FACE_NAMES[color]);
443
      }
444

    
445
    for(int i=0; i<len; i++)
446
      {
447
      row = i/size;
448
      col = i%size;
449

    
450
      cubitIndex = (size-1-row)*size + col;
451
      color = getCubitFaceColorIndex(cubitIndex,LEFT);
452
      objectString.append(FACE_NAMES[color]);
453
      }
454

    
455
    for(int i=0; i<len; i++)
456
      {
457
      row = i/size;
458
      col = i%size;
459

    
460
      if( col==size-1 ) cubitIndex = size*(size-1-row);
461
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
462
      else
463
        {
464
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
465
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
466
        }
467

    
468
      color = getCubitFaceColorIndex(cubitIndex,BACK);
469
      objectString.append(FACE_NAMES[color]);
470
      }
471

    
472
    return objectString.toString();
473
    }
474
}
(2-2/10)