Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyMegaminx.java @ 51df47f3

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 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.MatrixEffectQuaternion;
27
import org.distorted.library.main.DistortedEffects;
28
import org.distorted.library.main.DistortedTexture;
29
import org.distorted.library.mesh.MeshBase;
30
import org.distorted.library.mesh.MeshSquare;
31
import org.distorted.library.type.Static3D;
32
import org.distorted.library.type.Static4D;
33
import org.distorted.main.R;
34
import org.distorted.main.RubikSurfaceView;
35

    
36
import static org.distorted.objects.FactoryCubit.COS18;
37
import static org.distorted.objects.FactoryCubit.COS54;
38
import static org.distorted.objects.FactoryCubit.SIN18;
39
import static org.distorted.objects.FactoryCubit.SIN54;
40

    
41
///////////////////////////////////////////////////////////////////////////////////////////////////
42

    
43
public class TwistyMegaminx extends TwistyMinx
44
{
45
  static final float MEGA_D = 0.04f;
46

    
47
  private static final int NUM_CORNERS = 20;
48
  private static final int NUM_CENTERS = 12;
49
  private static final int NUM_EDGES   = 30;
50

    
51
  // the five vertices that form a given face. Order: the same as colors
52
  // of the faces in TwistyMinx.
53
  private static final int[][] mCenterMap =
54
         {
55
           { 0, 12,  4, 14,  2},
56
           { 0,  2, 18,  6, 16},
57
           { 6, 18, 11, 19,  7},
58
           { 3, 15,  9, 11, 19},
59
           { 4,  5, 15,  9, 14},
60
           { 1, 13,  5, 15,  3},
61
           { 1,  3, 19,  7, 17},
62
           {10, 16,  6,  7, 17},
63
           { 0, 12,  8, 10, 16},
64
           { 8, 13,  5,  4, 12},
65
           { 1, 13,  8, 10, 17},
66
           { 2, 14,  9, 11, 18},
67
         };
68

    
69
  // the quadruple ( vertex1, vertex2, face1, face2 ) defining an edge.
70
  // In fact the 2 vertices already define it, the faces only provide easy
71
  // way to get to know the colors. Order: arbitrary. Face1 arbitrarily on
72
  // the 'left' or right of vector vertex1 --> vertex2, according to Quat.
73
  private static final int[][] mEdgeMap =
74
         {
75
           {  0, 12,  0,  8},
76
           { 12,  4,  0,  9},
77
           {  4, 14,  0,  4},
78
           { 14,  2,  0, 11},
79
           {  2,  0,  0,  1},
80
           { 14,  9,  4, 11},
81
           {  9, 11,  3, 11},
82
           { 11, 18,  2, 11},
83
           { 18,  2,  1, 11},
84
           { 18,  6,  1,  2},
85
           {  6, 16,  1,  7},
86
           { 16,  0,  8,  1},
87
           { 16, 10,  7,  8},
88
           { 10,  8, 10,  8},
89
           {  8, 12,  9,  8},
90
           {  8, 13,  9, 10},
91
           { 13,  5,  9,  5},
92
           {  5,  4,  9,  4},
93
           {  5, 15,  5,  4},
94
           { 15,  9,  3,  4},
95
           { 11, 19,  2,  3},
96
           { 19,  7,  2,  6},
97
           {  7,  6,  2,  7},
98
           {  7, 17,  7,  6},
99
           { 17, 10,  7, 10},
100
           { 17,  1, 10,  6},
101
           {  1,  3,  5,  6},
102
           {  3, 19,  3,  6},
103
           {  1, 13, 10,  5},
104
           {  3, 15,  3,  5},
105
         };
106

    
107

    
108
  private static final int[] QUAT_EDGE_INDICES =
109
      {
110
        56, 40, 43, 59,  0, 55, 10, 17, 25, 49,
111
        48, 57, 18,  7, 53, 32, 20, 11, 31, 38,
112
        37, 30,  8, 28, 36, 44,  1, 46, 12, 14
113
      };
114

    
115
  private static final int[] QUAT_CENTER_INDICES =
116
      {
117
        16, 18, 22,  1, 20, 13, 14, 15,  0, 12,  2,  3
118
      };
119

    
120
  private static final float[][] mCenterCoords = new float[NUM_CENTERS][3];
121

    
122
  static
123
    {
124
    for(int center=0; center<NUM_CENTERS; center++)
125
      {
126
      int[] map = mCenterMap[center];
127

    
128
      float x = CORNERS[map[0]].get0() +
129
                CORNERS[map[1]].get0() +
130
                CORNERS[map[2]].get0() +
131
                CORNERS[map[3]].get0() +
132
                CORNERS[map[4]].get0() ;
133

    
134
      float y = CORNERS[map[0]].get1() +
135
                CORNERS[map[1]].get1() +
136
                CORNERS[map[2]].get1() +
137
                CORNERS[map[3]].get1() +
138
                CORNERS[map[4]].get1() ;
139

    
140
      float z = CORNERS[map[0]].get2() +
141
                CORNERS[map[1]].get2() +
142
                CORNERS[map[2]].get2() +
143
                CORNERS[map[3]].get2() +
144
                CORNERS[map[4]].get2() ;
145

    
146
      mCenterCoords[center][0] = x/5;
147
      mCenterCoords[center][1] = y/5;
148
      mCenterCoords[center][2] = z/5;
149
      }
150
    }
151

    
152
  private static MeshBase[] mCenterMeshes, mCornerMeshes;
153
  private static MeshBase[][] mEdgeMeshes;
154

    
155
  private static final Static4D[] mBasicCornerV, mCurrCornerV;
156

    
157
  static
158
    {
159
    mBasicCornerV = new Static4D[3];
160
    mCurrCornerV  = new Static4D[3];
161

    
162
    mBasicCornerV[0] = new Static4D( (SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
163
    mBasicCornerV[1] = new Static4D(-(SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
164
    mBasicCornerV[2] = new Static4D(              0,        -0.500f,    0.0f, 0.0f );
165
    };
166

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

    
169
  TwistyMegaminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
170
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
171
    {
172
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.MEGA, res, scrWidth);
173
    }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

    
177
  private int numCubitsPerCorner(int numLayers)
178
    {
179
    return 3*((numLayers-1)/2)*((numLayers-3)/2) + 1;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  private int numCubitsPerEdge(int numLayers)
185
    {
186
    return numLayers-2;
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  int getNumStickerTypes(int numLayers)
192
    {
193
    return (numLayers+3)/2;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  float[] getCuts(int numLayers)
199
    {
200
    float[] cuts = new float[numLayers-1];
201
    float D = numLayers*MovementMinx.DIST3D;
202
    float E = 2*C1;           // 2*cos(36 deg)
203
    float X = 2*D*E/(1+2*E);  // height of the 'upper' part of a dodecahedron, i.e. put it on a table,
204
                              // its height is then D*2*DIST3D, it has one 'lower' part of height X, one
205
                              // 'middle' part of height Y and one upper part of height X again.
206
                              // It's edge length = numLayers/3.0f.
207
    int num = (numLayers-1)/2;
208
    float G = X*(0.5f-MEGA_D)/num; // height of one Layer
209

    
210
    for(int i=0; i<num; i++)
211
      {
212
      cuts[        i] = -D + (i+0.5f)*G;
213
      cuts[2*num-1-i] = -cuts[i];
214
      }
215

    
216
    return cuts;
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  private void computeCenter(Static3D pos, int center, int numLayers)
222
    {
223
    float[] coords = mCenterCoords[center];
224
    float A = numLayers/3.0f;
225

    
226
    pos.set( A*coords[0], A*coords[1], A*coords[2] );
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230
// Fill out mCurrCorner{X,Y,Z} by applying appropriate Quat to mBasicCorner{X,Y,Z}
231
// Appropriate one: QUATS[QUAT_INDICES[corner]].
232

    
233
  private void computeBasicCornerVectors(int corner)
234
    {
235
    Static4D quat = QUATS[QUAT_CORNER_INDICES[corner]];
236

    
237
    mCurrCornerV[0] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[0],quat);
238
    mCurrCornerV[1] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[1],quat);
239
    mCurrCornerV[2] = RubikSurfaceView.rotateVectorByQuat(mBasicCornerV[2],quat);
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243

    
244
  private void computeCorner(Static3D pos, int numCubitsPerCorner, int numLayers, int corner, int part)
245
    {
246
    float D = numLayers/3.0f;
247
    Static3D corn = CORNERS[corner];
248

    
249
    if( part==0 )
250
      {
251
      pos.set( corn.get0()*D, corn.get1()*D, corn.get2()*D );
252
      }
253
    else
254
      {
255
      float E = 2.0f*(numLayers/3.0f)*(0.5f-MEGA_D)/(0.5f*(numLayers-1));
256
      int N = (numCubitsPerCorner-1)/3;
257
      int block = (part-1) % N;
258
      int index = (part-1) / N;
259
      Static4D pri = mCurrCornerV[index];
260
      Static4D sec = mCurrCornerV[(index+2)%3];
261

    
262
      int multP = (block % ((numLayers-3)/2)) + 1;
263
      int multS = (block / ((numLayers-3)/2));
264

    
265
      pos.set( corn.get0()*D + (pri.get0()*multP + sec.get0()*multS)*E,
266
               corn.get1()*D + (pri.get1()*multP + sec.get1()*multS)*E,
267
               corn.get2()*D + (pri.get2()*multP + sec.get2()*multS)*E );
268
      }
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  private int computeEdgeType(int cubit, int numCubitsPerCorner, int numCubitsPerEdge)
274
    {
275
    int part = (cubit - NUM_CORNERS*numCubitsPerCorner) % numCubitsPerEdge;
276
    return (part+1)/2;
277
    }
278

    
279
///////////////////////////////////////////////////////////////////////////////////////////////////
280

    
281
  private void computeEdge(Static3D pos, int numLayers, int edge, int part)
282
    {
283
    float corr = numLayers/3.0f;
284

    
285
    Static3D c1 = CORNERS[ mEdgeMap[edge][0] ];
286
    Static3D c2 = CORNERS[ mEdgeMap[edge][1] ];
287
    float x = corr*(c1.get0() + c2.get0())/2;
288
    float y = corr*(c1.get1() + c2.get1())/2;
289
    float z = corr*(c1.get2() + c2.get2())/2;
290

    
291
    if( part==0 )
292
      {
293
      pos.set(x,y,z);
294
      }
295
    else
296
      {
297
      int mult = (part+1)/2;
298
      int dir  = (part+1)%2;
299
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
300

    
301
      float vX = corr*center[0] - x;
302
      float vY = corr*center[1] - y;
303
      float vZ = corr*center[2] - z;
304

    
305
      float len = (float)Math.sqrt(vX*vX+vY*vY+vZ*vZ);
306
      float A = mult*corr*(0.5f-MEGA_D)*COS18/((numLayers-1)*0.5f)/len;
307

    
308
      pos.set( x+A*vX, y+A*vY, z+A*vZ );
309
      }
310
    }
311

    
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313

    
314
  Static3D[] getCubitPositions(int numLayers)
315
    {
316
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
317
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
318
    int numCubits = NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge + NUM_CENTERS;
319
    int index=0;
320

    
321
    final Static3D[] CENTERS = new Static3D[numCubits];
322

    
323
    for(int corner=0; corner<NUM_CORNERS; corner++)
324
      {
325
      computeBasicCornerVectors(corner);
326

    
327
      for(int part=0; part<numCubitsPerCorner; part++, index++)
328
        {
329
        CENTERS[index] = new Static3D(0,0,0);
330
        computeCorner(CENTERS[index],numCubitsPerCorner,numLayers,corner,part);
331
        }
332
      }
333

    
334
    for(int edge=0; edge<NUM_EDGES; edge++)
335
      {
336
      for(int part=0; part<numCubitsPerEdge; part++, index++)
337
        {
338
        CENTERS[index] = new Static3D(0,0,0);
339
        computeEdge(CENTERS[index], numLayers, edge, part );
340
        }
341
      }
342

    
343
    for(int center=0; center<NUM_CENTERS; center++, index++)
344
      {
345
      CENTERS[index] = new Static3D(0,0,0);
346
      computeCenter(CENTERS[index], center, numLayers);
347
      }
348

    
349
    return CENTERS;
350
    }
351

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

    
354
  private int getQuat(int cubit, int numCubitsPerCorner, int numCubitsPerEdge)
355
    {
356
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
357
      {
358
      int corner = cubit/numCubitsPerCorner;
359
      return QUAT_CORNER_INDICES[corner];
360
      }
361

    
362
    if( cubit < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
363
      {
364
      int edge = (cubit-NUM_CORNERS*numCubitsPerCorner)/numCubitsPerEdge;
365
      return QUAT_EDGE_INDICES[edge];
366
      }
367

    
368
    int center = cubit - NUM_CORNERS*numCubitsPerCorner - NUM_EDGES*numCubitsPerEdge;
369
    return QUAT_CENTER_INDICES[center];
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373

    
374
  MeshBase createCubitMesh(int cubit, int numLayers)
375
    {
376
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
377
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
378
    int index = (numLayers-3)/2;
379
    int[] sizes = ObjectList.MEGA.getSizes();
380
    int variants = sizes.length;
381
    MeshBase mesh;
382

    
383
    if( mCornerMeshes==null ) mCornerMeshes = new MeshBase[variants];
384
    if( mEdgeMeshes  ==null ) mEdgeMeshes   = new MeshBase[variants][(sizes[variants-1]-1)/2];
385
    if( mCenterMeshes==null ) mCenterMeshes = new MeshBase[variants];
386

    
387
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
388
      {
389
      if( mCornerMeshes[index]==null )
390
        {
391
        mCornerMeshes[index] = FactoryCubit.getInstance().createMegaminxCornerMesh(numLayers);
392
        }
393
      mesh = mCornerMeshes[index].copy(true);
394
      }
395
    else if( cubit<NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
396
      {
397
      int type = computeEdgeType(cubit,numCubitsPerCorner,numCubitsPerEdge);
398

    
399
      if( mEdgeMeshes[index][type]==null )
400
        {
401
        float height= (numLayers/3.0f)*(0.5f-MEGA_D)*COS18/((numLayers-1)*0.5f);
402
        float width = (numLayers/3.0f)*2*MEGA_D + 2*type*height*SIN18/COS18;
403

    
404
        mEdgeMeshes[index][type] = FactoryCubit.getInstance().createMegaminxEdgeMesh(width,height);
405
        }
406

    
407
      mesh = mEdgeMeshes[index][type].copy(true);
408
      }
409
    else
410
      {
411
      if( mCenterMeshes[index]==null )
412
        {
413
        float width = 2 * (numLayers/3.0f) * (MEGA_D+(0.5f-MEGA_D)*SIN18);
414
        mCenterMeshes[index] = FactoryCubit.getInstance().createMegaminxCenterMesh(width);
415
        }
416

    
417
      mesh = mCenterMeshes[index].copy(true);
418
      }
419

    
420
    Static4D q = QUATS[getQuat(cubit,numCubitsPerCorner,numCubitsPerEdge)];
421
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( q, new Static3D(0,0,0) );
422
    mesh.apply(quat,0xffffffff,0);
423

    
424
    return mesh;
425
    }
426

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

    
429
  int getCornerColor(int cubit, int cubitface, int numLayers, int numCubitsPerCorner)
430
    {
431
    if( cubitface<0 || cubitface>2 ) return NUM_TEXTURES;
432

    
433
    int part  = cubit % numCubitsPerCorner;
434
    int corner= cubit / numCubitsPerCorner;
435

    
436
    if( part==0 )
437
      {
438
      return mCornerFaceMap[corner][cubitface];
439
      }
440
    else
441
      {
442
      int N = (numCubitsPerCorner-1)/3;
443
      int block = (part-1) % N;
444
      int index = (part-1) / N;
445

    
446
      if( block< (numLayers-3)/2 )
447
        {
448
        switch(index)
449
          {
450
          case 0: return cubitface==1 ? NUM_TEXTURES : mCornerFaceMap[corner][cubitface];
451
          case 1: return cubitface==0 ? NUM_TEXTURES : mCornerFaceMap[corner][cubitface];
452
          case 2: return cubitface==2 ? NUM_TEXTURES : mCornerFaceMap[corner][cubitface];
453
          }
454
        }
455
      else
456
        {
457
        switch(index)
458
          {
459
          case 0: return cubitface==0 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES;
460
          case 1: return cubitface==2 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES;
461
          case 2: return cubitface==1 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES;
462
          }
463
        }
464
      }
465

    
466
    return NUM_TEXTURES;
467
    }
468

    
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470

    
471
  int getEdgeColor(int edge, int cubitface, int numCubitsPerEdge)
472
    {
473
    if( cubitface<0 || cubitface>1 ) return NUM_TEXTURES;
474

    
475
    int part    = edge % numCubitsPerEdge;
476
    int variant = edge / numCubitsPerEdge;
477

    
478
    return (part==0 || cubitface==((part+1)%2)) ? mEdgeMap[variant][cubitface+2] + ((part+3)/2)*NUM_FACES : NUM_TEXTURES;
479
    }
480

    
481
///////////////////////////////////////////////////////////////////////////////////////////////////
482

    
483
  int getCenterColor(int center, int cubitface, int numLayers)
484
    {
485
    return cubitface>0 ? NUM_TEXTURES : center + NUM_FACES*(numLayers+1)/2;
486
    }
487

    
488
///////////////////////////////////////////////////////////////////////////////////////////////////
489

    
490
  int getFaceColor(int cubit, int cubitface, int numLayers)
491
    {
492
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
493
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
494

    
495
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
496
      {
497
      return getCornerColor(cubit,cubitface,numLayers,numCubitsPerCorner);
498
      }
499
    else if( cubit<NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
500
      {
501
      int edge = cubit - NUM_CORNERS*numCubitsPerCorner;
502
      return getEdgeColor(edge,cubitface,numCubitsPerEdge);
503
      }
504
    else
505
      {
506
      int center = cubit-NUM_CORNERS*numCubitsPerCorner-NUM_EDGES*numCubitsPerEdge;
507
      return getCenterColor( center, cubitface, numLayers);
508
      }
509
    }
510

    
511
///////////////////////////////////////////////////////////////////////////////////////////////////
512

    
513
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
514
    {
515
    int COLORS = FACE_COLORS.length;
516
    float R,S;
517
    float[] vertices;
518

    
519
    int variant = face/COLORS;
520

    
521
    if( variant==0 )
522
      {
523
      float Y = COS54/(2*SIN54);
524
      R = 0.06f;
525
      S = 0.065f;
526
      vertices = new float[] { -0.5f, 0.0f, 0.0f, -Y, 0.5f, 0.0f, 0.0f, Y };
527
      }
528
    else
529
      {
530
      int numLayers = getNumLayers();
531
      float height= (numLayers/3.0f)*(0.5f-MEGA_D)*COS18/((numLayers-1)*0.5f);
532
      float W = height*SIN18/COS18;
533
      float width = (numLayers/3.0f)*2*MEGA_D + 2*(variant-1)*W;
534

    
535
      if( variant < (numLayers+1)/2 )
536
        {
537
        float X1 = 0.5f*height;
538
        float Y1 = 0.5f*width;
539
        float Y2 = 0.5f*width + W;
540

    
541
        R = 0.04f;
542
        S = 0.05f;
543
        vertices = new float[] { -X1, Y1, -X1, -Y1, X1, -Y2, X1, Y2 };
544
        }
545
      else
546
        {
547
        float Z = width/(2*COS54);
548
        float X1 = 0.5f*width;
549
        float Y1 = Z*SIN54;
550
        float X2 = Z*COS18;
551
        float Y2 = Z*SIN18;
552

    
553
        R = 0.05f;
554
        S = 0.05f;
555
        vertices = new float[] { -X1,+Y1, -X2,-Y2, 0.0f,-Z, +X2,-Y2, +X1,+Y1 };
556
        }
557
      }
558

    
559
    FactorySticker factory = FactorySticker.getInstance();
560
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face%COLORS], R);
561
    }
562

    
563
///////////////////////////////////////////////////////////////////////////////////////////////////
564
// PUBLIC API
565

    
566
  public boolean isSolved()
567
    {
568
    int index = CUBITS[0].mQuatIndex;
569

    
570
    for(int i=1; i<NUM_CUBITS; i++)
571
      {
572
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
573
      }
574

    
575
    return true;
576
    }
577

    
578
///////////////////////////////////////////////////////////////////////////////////////////////////
579
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
580
// then if it were rotated by quaternion 'quat'.
581
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
582
// middle squares get interchanged. No visible difference!
583
//
584
// So: this is true iff the cubit
585
// a) is a corner or edge and the quaternions are the same
586
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
587

    
588
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
589
    {
590
    if ( cubit.mQuatIndex == quatIndex ) return true;
591

    
592
    int belongsToHowManyFaces = 0;
593
    int lastLayer = getNumLayers()-1;
594
    float row;
595
    final float MAX_ERROR = 0.01f;
596

    
597
    for(int i=0; i<NUM_AXIS; i++)
598
      {
599
      row = cubit.mRotationRow[i];
600
      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
601
          (row-lastLayer<MAX_ERROR && row-lastLayer>-MAX_ERROR)  ) belongsToHowManyFaces++;
602
      }
603

    
604
    switch(belongsToHowManyFaces)
605
      {
606
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
607
      case 1 :                // cubit that lies inside one of the faces
608
               Static3D orig = cubit.getOrigPosition();
609
               Static4D quat1 = QUATS[quatIndex];
610
               Static4D quat2 = QUATS[cubit.mQuatIndex];
611

    
612
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
613
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
614
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
615

    
616
               float row1, row2;
617
               float x1 = rotated1.get0();
618
               float y1 = rotated1.get1();
619
               float z1 = rotated1.get2();
620
               float x2 = rotated2.get0();
621
               float y2 = rotated2.get1();
622
               float z2 = rotated2.get2();
623

    
624
               for(int i=0; i<NUM_AXIS; i++)
625
                 {
626
                 row1 = computeRow(x1,y1,z1,i);
627
                 row2 = computeRow(x2,y2,z2,i);
628

    
629
                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return true;
630
                 }
631
               return false;
632

    
633
      default: return false;  // edge or corner
634
      }
635
    }
636

    
637
///////////////////////////////////////////////////////////////////////////////////////////////////
638

    
639
  public int getObjectName(int numLayers)
640
    {
641
    if( numLayers==3 ) return R.string.minx3;
642
    if( numLayers==5 ) return R.string.minx4;
643

    
644
    return 0;
645
    }
646

    
647
///////////////////////////////////////////////////////////////////////////////////////////////////
648

    
649
  public int getInventor(int numLayers)
650
    {
651
    if( numLayers==3 ) return R.string.minx3_inventor;
652
    if( numLayers==5 ) return R.string.minx4_inventor;
653

    
654
    return 0;
655
    }
656

    
657
///////////////////////////////////////////////////////////////////////////////////////////////////
658

    
659
  public int getComplexity(int numLayers)
660
    {
661
    if( numLayers==3 ) return 4;
662

    
663
    return 5;
664
    }
665
}
(24-24/30)