Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / RubikCube.java @ 8f53e513

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[] 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
  private static final int[] FACE_COLORS = new int[]
54
         {
55
           0xffffff00, 0xffffffff,   // AXIS[0]right (right-YELLOW) AXIS[0]left (left  -WHITE)
56
           0xff0000ff, 0xff00ff00,   // AXIS[1]right (top  -BLUE  ) AXIS[1]left (bottom-GREEN)
57
           0xffff0000, 0xffb5651d    // AXIS[2]right (front-RED   ) AXIS[2]left (back  -BROWN)
58
         };
59

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

    
74
         new Static4D( SQ2/2,  SQ2/2,  0.0f ,   0.0f),
75
         new Static4D( SQ2/2, -SQ2/2,  0.0f ,   0.0f),
76
         new Static4D( SQ2/2,   0.0f,  SQ2/2,   0.0f),
77
         new Static4D(-SQ2/2,   0.0f,  SQ2/2,   0.0f),
78
         new Static4D( SQ2/2,   0.0f,   0.0f,  SQ2/2),
79
         new Static4D( SQ2/2,   0.0f,   0.0f, -SQ2/2),
80
         new Static4D(  0.0f,  SQ2/2,  SQ2/2,   0.0f),
81
         new Static4D(  0.0f,  SQ2/2, -SQ2/2,   0.0f),
82
         new Static4D(  0.0f,  SQ2/2,   0.0f,  SQ2/2),
83
         new Static4D(  0.0f,  SQ2/2,   0.0f, -SQ2/2),
84
         new Static4D(  0.0f,   0.0f,  SQ2/2,  SQ2/2),
85
         new Static4D(  0.0f,   0.0f,  SQ2/2, -SQ2/2),
86

    
87
         new Static4D(  0.5f,   0.5f,   0.5f,   0.5f),
88
         new Static4D(  0.5f,   0.5f,  -0.5f,   0.5f),
89
         new Static4D(  0.5f,   0.5f,  -0.5f,  -0.5f),
90
         new Static4D(  0.5f,  -0.5f,   0.5f,  -0.5f),
91
         new Static4D( -0.5f,  -0.5f,  -0.5f,   0.5f),
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
         };
96

    
97
  private static MeshBase[] mMeshes;
98

    
99
///////////////////////////////////////////////////////////////////////////////////////////////////
100

    
101
  RubikCube(int size, Static4D quat, DistortedTexture texture,
102
            MeshRectangles mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
103
    {
104
    super(size, 60, quat, texture, mesh, effects, moves, RubikObjectList.CUBE, res, scrWidth);
105
    }
106

    
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108
// paint the square with upper-right corner at (left,top) and side length 'side' with texture
109
// for face 'face'.
110

    
111
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
112
    {
113
    final float R = side*0.10f;
114
    final float M = side*0.05f;
115

    
116
    paint.setColor(FACE_COLORS[face]);
117
    canvas.drawRoundRect( left+M, top+M, left+side-M, top+side-M, R, R, paint);
118
    }
119

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121

    
122
  Static3D[] getCubitPositions(int size)
123
    {
124
    int numCubits = size>1 ? 6*size*size - 12*size + 8 : 1;
125
    Static3D[] tmp = new Static3D[numCubits];
126

    
127
    float diff = 0.5f*(size-1);
128
    int currentPosition = 0;
129

    
130
    for(int x = 0; x<size; x++)
131
      for(int y = 0; y<size; y++)
132
        for(int z = 0; z<size; z++)
133
          if( x==0 || x==size-1 || y==0 || y==size-1 || z==0 || z==size-1 )
134
            {
135
            tmp[currentPosition++] = new Static3D(x-diff,y-diff,z-diff);
136
            }
137

    
138
    return tmp;
139
    }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  Static4D[] getQuats()
144
    {
145
    return QUATS;
146
    }
147

    
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149

    
150
  int getNumFaces()
151
    {
152
    return FACE_COLORS.length;
153
    }
154

    
155
///////////////////////////////////////////////////////////////////////////////////////////////////
156

    
157
  int getNumCubitFaces()
158
    {
159
    return FACE_COLORS.length;
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163

    
164
  float getScreenRatio()
165
    {
166
    return 0.5f;
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

    
171
  MeshBase createCubitMesh(int cubit)
172
    {
173
    int size   = getSize();
174
    int ordinal= RubikObjectList.CUBE.ordinal();
175
    int index  = RubikObjectList.getSizeIndex(ordinal,size);
176
    float[] loc;
177

    
178
    switch(size)
179
      {
180
      case 5 : loc = new float[] { -0.5f, 0.05f, 0.1f, 0.35f, 0.35f, 0.1f, 0.05f };
181
               break;
182
      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 };
183
               break;
184
      default: loc = new float[] { -0.5f, 0.04f, 0.06f, 0.25f, 0.30f, 0.25f, 0.06f, 0.04f };
185
      }
186

    
187
    return createCubitMesh(index,loc);
188
    }
189

    
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191

    
192
  MeshBase createCubitMesh(int index, float[] loc)
193
    {
194
    if( mMeshes==null )
195
      {
196
      mMeshes = new MeshBase[RubikObjectList.CUBE.getNumVariants()];
197
      }
198

    
199
    if( mMeshes[index]==null )
200
      {
201
      final int MESHES=6;
202
      int association = 1;
203
      MeshBase[] meshes = new MeshRectangles[MESHES];
204
      meshes[0] = new MeshRectangles(loc,loc);
205
      meshes[0].setEffectAssociation(0,association,0);
206

    
207
      for(int i=1; i<MESHES; i++)
208
        {
209
        association <<=1;
210
        meshes[i] = meshes[0].copy(true);
211
        meshes[i].setEffectAssociation(0,association,0);
212
        }
213

    
214
      mMeshes[index] = new MeshJoined(meshes);
215

    
216
      Static3D axisY   = new Static3D(0,1,0);
217
      Static3D axisX   = new Static3D(1,0,0);
218
      Static3D center  = new Static3D(0,0,0);
219
      Static1D angle90 = new Static1D(90);
220
      Static1D angle180= new Static1D(180);
221
      Static1D angle270= new Static1D(270);
222

    
223
      float d1 = 1.0f;
224
      float d2 =-0.05f;
225
      float d3 = 0.12f;
226

    
227
      Static3D dCen0 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(+0.5f) );
228
      Static3D dCen1 = new Static3D( d1*(+0.5f), d1*(+0.5f), d1*(-0.5f) );
229
      Static3D dCen2 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(+0.5f) );
230
      Static3D dCen3 = new Static3D( d1*(+0.5f), d1*(-0.5f), d1*(-0.5f) );
231
      Static3D dCen4 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(+0.5f) );
232
      Static3D dCen5 = new Static3D( d1*(-0.5f), d1*(+0.5f), d1*(-0.5f) );
233
      Static3D dCen6 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(+0.5f) );
234
      Static3D dCen7 = new Static3D( d1*(-0.5f), d1*(-0.5f), d1*(-0.5f) );
235

    
236
      Static3D dVec0 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(+0.5f) );
237
      Static3D dVec1 = new Static3D( d2*(+0.5f), d2*(+0.5f), d2*(-0.5f) );
238
      Static3D dVec2 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(+0.5f) );
239
      Static3D dVec3 = new Static3D( d2*(+0.5f), d2*(-0.5f), d2*(-0.5f) );
240
      Static3D dVec4 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(+0.5f) );
241
      Static3D dVec5 = new Static3D( d2*(-0.5f), d2*(+0.5f), d2*(-0.5f) );
242
      Static3D dVec6 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(+0.5f) );
243
      Static3D dVec7 = new Static3D( d2*(-0.5f), d2*(-0.5f), d2*(-0.5f) );
244

    
245
      Static4D dReg  = new Static4D(0,0,0,d3);
246
      Static1D dRad  = new Static1D(1);
247

    
248
      VertexEffectMove   effect0 = new VertexEffectMove(new Static3D(0,0,+0.5f));
249
      effect0.setMeshAssociation(63,-1);  // all 6 sides
250
      VertexEffectRotate effect1 = new VertexEffectRotate( angle180, axisX, center );
251
      effect1.setMeshAssociation(32,-1);  // back
252
      VertexEffectRotate effect2 = new VertexEffectRotate( angle90 , axisX, center );
253
      effect2.setMeshAssociation( 8,-1);  // bottom
254
      VertexEffectRotate effect3 = new VertexEffectRotate( angle270, axisX, center );
255
      effect3.setMeshAssociation( 4,-1);  // top
256
      VertexEffectRotate effect4 = new VertexEffectRotate( angle270, axisY, center );
257
      effect4.setMeshAssociation( 2,-1);  // left
258
      VertexEffectRotate effect5 = new VertexEffectRotate( angle90 , axisY, center );
259
      effect5.setMeshAssociation( 1,-1);  // right
260

    
261
      VertexEffectDeform effect6 = new VertexEffectDeform(dVec0, dRad, dCen0, dReg);
262
      VertexEffectDeform effect7 = new VertexEffectDeform(dVec1, dRad, dCen1, dReg);
263
      VertexEffectDeform effect8 = new VertexEffectDeform(dVec2, dRad, dCen2, dReg);
264
      VertexEffectDeform effect9 = new VertexEffectDeform(dVec3, dRad, dCen3, dReg);
265
      VertexEffectDeform effect10= new VertexEffectDeform(dVec4, dRad, dCen4, dReg);
266
      VertexEffectDeform effect11= new VertexEffectDeform(dVec5, dRad, dCen5, dReg);
267
      VertexEffectDeform effect12= new VertexEffectDeform(dVec6, dRad, dCen6, dReg);
268
      VertexEffectDeform effect13= new VertexEffectDeform(dVec7, dRad, dCen7, dReg);
269

    
270
      VertexEffectSink   effect14= new VertexEffectSink( new Static1D(1.5f), center, new Static4D(0,0,0,0.72f) );
271

    
272
      mMeshes[index].apply(effect0);
273
      mMeshes[index].apply(effect1);
274
      mMeshes[index].apply(effect2);
275
      mMeshes[index].apply(effect3);
276
      mMeshes[index].apply(effect4);
277
      mMeshes[index].apply(effect5);
278
      mMeshes[index].apply(effect6);
279
      mMeshes[index].apply(effect7);
280
      mMeshes[index].apply(effect8);
281
      mMeshes[index].apply(effect9);
282
      mMeshes[index].apply(effect10);
283
      mMeshes[index].apply(effect11);
284
      mMeshes[index].apply(effect12);
285
      mMeshes[index].apply(effect13);
286
      mMeshes[index].apply(effect14);
287

    
288
      mMeshes[index].mergeEffComponents();
289
      }
290

    
291
    return mMeshes[index].copy(true);
292
    }
293

    
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295
// PUBLIC API
296

    
297
  public Static3D[] getRotationAxis()
298
    {
299
    return AXIS;
300
    }
301

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303

    
304
  public int getBasicAngle()
305
    {
306
    return 4;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

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

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317

    
318
  public float[] getRowChances()
319
    {
320
    int size = getSize();
321
    float[] chances = new float[size];
322

    
323
    for(int i=0; i<size; i++)
324
      {
325
      chances[i] = (i+1.0f) / size;
326
      }
327

    
328
    return chances;
329
    }
330

    
331
///////////////////////////////////////////////////////////////////////////////////////////////////
332

    
333
  public float returnRotationFactor(float offset)
334
    {
335
    return 1.0f;
336
    }
337

    
338
///////////////////////////////////////////////////////////////////////////////////////////////////
339
// order: Up --> Right --> Front --> Down --> Left --> Back
340
// (because the first implemented Solver - the two-phase Cube3 one - expects such order)
341
//
342
// Solved 3x3x3 Cube maps to "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB"
343
//
344
// s : size of the cube; let index = a*s + b    (i.e. a,b = row,column)
345
//
346
// Up    :   index --> b<s-1 ? (s-1)*(s+4b)+a : 6*s*s -13*s +8 +a
347
// Right :   index --> 6*s*s - 12*s + 7 - index
348
// Front :   index --> if b==0  : s*s - 1 - index
349
//                     if b==s-1: 6*s*s -11*s +6 - index
350
//                     else
351
//                         a==0: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-2) + s
352
//                         else: s*s + s-1 + 4*(b-1)*(s-1) + 2*(s-1-a)
353
// Down  :   index --> b==0 ? (s-1-a) : s*s + s-1 + 4*(b-1)*(s-1) - a
354
// Left  :   index --> (s-1-a)*s + b
355
// Back  :   index --> if b==s-1: s*(s-1-a)
356
//                     if b==0  : 5*s*s -12*s + 8 + (s-1-a)*s
357
//                     else
358
//                        if a==s-1: s*s + 4*(s-2-b)*(s-1)
359
//                        else     : s*s + 4*(s-2-b)*(s-1) + s + (s-2-a)*2
360

    
361
  public String retObjectString()
362
    {
363
    StringBuilder objectString = new StringBuilder();
364
    int size = getSize();
365
    int len = size*size;
366
    int cubitIndex, row, col;
367
    int color;
368

    
369
    final int RIGHT= 0;
370
    final int LEFT = 1;
371
    final int UP   = 2;
372
    final int DOWN = 3;
373
    final int FRONT= 4;
374
    final int BACK = 5;
375

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

    
378
    for(int i=0; i<len; i++)
379
      {
380
      row = i/size;
381
      col = i%size;
382

    
383
      cubitIndex = col<size-1 ? (size-1)*(size+4*col) + row : 6*size*size - 13*size + 8 + row;
384
      color = getCubitFaceColorIndex(cubitIndex,UP);
385
      objectString.append(FACE_NAMES[color]);
386
      }
387

    
388
    for(int i=0; i<len; i++)
389
      {
390
      cubitIndex = 6*size*size - 12*size +7 - i;
391
      color = getCubitFaceColorIndex(cubitIndex,RIGHT);
392
      objectString.append(FACE_NAMES[color]);
393
      }
394

    
395
    for(int i=0; i<len; i++)
396
      {
397
      row = i/size;
398
      col = i%size;
399

    
400
      if( col==size-1 ) cubitIndex = 6*size*size - 11*size + 6 -i;
401
      else if( col==0 ) cubitIndex = size*size - 1 - i;
402
      else
403
        {
404
        if( row==0 ) cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-2) + size;
405
        else         cubitIndex = size*size + size-1 + 4*(col-1)*(size-1) + 2*(size-1-row);
406
        }
407

    
408
      color = getCubitFaceColorIndex(cubitIndex,FRONT);
409
      objectString.append(FACE_NAMES[color]);
410
      }
411

    
412
    for(int i=0; i<len; i++)
413
      {
414
      row = i/size;
415
      col = i%size;
416

    
417
      cubitIndex = col==0 ? size-1-row : size*size + size-1 + 4*(col-1)*(size-1) - row;
418
      color = getCubitFaceColorIndex(cubitIndex,DOWN);
419
      objectString.append(FACE_NAMES[color]);
420
      }
421

    
422
    for(int i=0; i<len; i++)
423
      {
424
      row = i/size;
425
      col = i%size;
426

    
427
      cubitIndex = (size-1-row)*size + col;
428
      color = getCubitFaceColorIndex(cubitIndex,LEFT);
429
      objectString.append(FACE_NAMES[color]);
430
      }
431

    
432
    for(int i=0; i<len; i++)
433
      {
434
      row = i/size;
435
      col = i%size;
436

    
437
      if( col==size-1 ) cubitIndex = size*(size-1-row);
438
      else if( col==0 ) cubitIndex = 5*size*size - 12*size + 8 + (size-1-row)*size;
439
      else
440
        {
441
        if( row==size-1 ) cubitIndex = size*size + 4*(size-2-col)*(size-1);
442
        else              cubitIndex = size*size + 4*(size-2-col)*(size-1) + size + 2*(size-2-row);
443
        }
444

    
445
      color = getCubitFaceColorIndex(cubitIndex,BACK);
446
      objectString.append(FACE_NAMES[color]);
447
      }
448

    
449
    return objectString.toString();
450
    }
451
}
(2-2/10)