Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyDiamond.java @ 2ef489e2

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 java.util.Random;
37

    
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39

    
40
public class TwistyDiamond extends TwistyObject
41
{
42
  private static final int FACES_PER_CUBIT =8;
43

    
44
  // the four rotation axis of a Diamond. Must be normalized.
45
  static final Static3D[] ROT_AXIS = new Static3D[]
46
         {
47
           new Static3D(+SQ6/3,+SQ3/3,     0),
48
           new Static3D(-SQ6/3,+SQ3/3,     0),
49
           new Static3D(     0,-SQ3/3,-SQ6/3),
50
           new Static3D(     0,-SQ3/3,+SQ6/3)
51
         };
52

    
53
  private static final int[] FACE_COLORS = new int[]
54
         {
55
           COLOR_ORANGE, COLOR_VIOLET,
56
           COLOR_WHITE , COLOR_BLUE  ,
57
           COLOR_YELLOW, COLOR_RED   ,
58
           COLOR_GREEN , COLOR_GREY
59
         };
60

    
61
  // All legal rotation quats of a Diamond: unit + three 180 deg turns + 8 generators
62
  private static final Static4D[] QUATS = new Static4D[]
63
         {
64
           new Static4D(  0.0f,  0.0f,   0.0f,  1.0f ),
65
           new Static4D(  0.0f,  1.0f,   0.0f,  0.0f ),
66
           new Static4D(+SQ2/2,  0.0f, -SQ2/2,  0.0f ),
67
           new Static4D(-SQ2/2,  0.0f, -SQ2/2,  0.0f ),
68

    
69
           new Static4D(+SQ2/2,  0.5f,   0.0f,  0.5f ),
70
           new Static4D(-SQ2/2,  0.5f,   0.0f,  0.5f ),
71
           new Static4D(  0.0f,  0.5f, +SQ2/2,  0.5f ),
72
           new Static4D(  0.0f,  0.5f, -SQ2/2,  0.5f ),
73
           new Static4D(+SQ2/2,  0.5f,   0.0f, -0.5f ),
74
           new Static4D(-SQ2/2,  0.5f,   0.0f, -0.5f ),
75
           new Static4D(  0.0f,  0.5f, +SQ2/2, -0.5f ),
76
           new Static4D(  0.0f,  0.5f, -SQ2/2, -0.5f )
77
         };
78

    
79
  private static final float DIST = 0.50f;
80

    
81
  private static final int[][] mFaceNeutralQuatIndex = new int[][]
82
         {
83
             {6,10},
84
             {4, 8},
85
             {7,11},
86
             {5, 9},
87
             {7,11},
88
             {5, 9},
89
             {6,10},
90
             {4, 8}
91
         };
92

    
93
  private static MeshBase mOctaMesh, mTetraMesh;
94

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96

    
97
  TwistyDiamond(int size, Static4D quat, DistortedTexture texture,
98
                MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
99
    {
100
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.DIAM, res, scrWidth);
101
    }
102

    
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104

    
105
  float getScreenRatio()
106
    {
107
    return 0.65f;
108
    }
109

    
110
///////////////////////////////////////////////////////////////////////////////////////////////////
111

    
112
  Static4D[] getQuats()
113
    {
114
    return QUATS;
115
    }
116

    
117
///////////////////////////////////////////////////////////////////////////////////////////////////
118

    
119
  int getNumFaces()
120
    {
121
    return FACE_COLORS.length;
122
    }
123

    
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125

    
126
  boolean shouldResetTextureMaps()
127
    {
128
    return false;
129
    }
130

    
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132

    
133
  int getNumStickerTypes(int numLayers)
134
    {
135
    return 1;
136
    }
137

    
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139

    
140
  float[] getCuts(int numLayers)
141
    {
142
    if( numLayers<2 )
143
      {
144
      return null;
145
      }
146
    else
147
      {
148
      float[] cuts = new float[numLayers-1];
149
      float dist = SQ6*0.666f;
150
      float cut  = 0.5f*dist*(2-numLayers);
151

    
152
      for(int i=0; i<numLayers-1; i++)
153
        {
154
        cuts[i] = cut;
155
        cut += dist;
156
        }
157

    
158
      return cuts;
159
      }
160
    }
161

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

    
164
  int getNumCubitFaces()
165
    {
166
    return FACES_PER_CUBIT;
167
    }
168

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

    
171
  private int getNumOctahedrons(int layers)
172
    {
173
    return layers==1 ? 1 : 4*(layers-1)*(layers-1) + 2;
174
    }
175

    
176
///////////////////////////////////////////////////////////////////////////////////////////////////
177

    
178
  private int getNumTetrahedrons(int layers)
179
    {
180
    return 4*layers*(layers-1);
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

    
185
  private int createOctahedrons(float[][] centers, int index, int layers, float height)
186
    {
187
    float x = DIST*(layers-1);
188
    float z = DIST*(layers+1);
189

    
190
    for(int i=0; i<layers; i++, index++)
191
      {
192
      z -= 2*DIST;
193
      centers[index][0] = x;
194
      centers[index][1] = height;
195
      centers[index][2] = z;
196
      }
197

    
198
    for(int i=0; i<layers-1; i++, index++)
199
      {
200
      x -= 2*DIST;
201
      centers[index][0] = x;
202
      centers[index][1] = height;
203
      centers[index][2] = z;
204
      }
205

    
206
    for(int i=0; i<layers-1; i++, index++)
207
      {
208
      z += 2*DIST;
209
      centers[index][0] = x;
210
      centers[index][1] = height;
211
      centers[index][2] = z;
212
      }
213

    
214
    for(int i=0; i<layers-2; i++, index++)
215
      {
216
      x += 2*DIST;
217
      centers[index][0] = x;
218
      centers[index][1] = height;
219
      centers[index][2] = z;
220
      }
221

    
222
    return index;
223
    }
224

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226

    
227
  private int createTetrahedrons(float[][] centers, int index, int layers, float height)
228
    {
229
    float x = DIST*(layers-1);
230
    float z = DIST*layers;
231

    
232
    for(int i=0; i<layers-1; i++, index++)
233
      {
234
      z -= 2*DIST;
235
      centers[index][0] = x;
236
      centers[index][1] = height;
237
      centers[index][2] = z;
238
      }
239

    
240
    x += DIST;
241
    z -= DIST;
242

    
243
    for(int i=0; i<layers-1; i++, index++)
244
      {
245
      x -= 2*DIST;
246
      centers[index][0] = x;
247
      centers[index][1] = height;
248
      centers[index][2] = z;
249
      }
250

    
251
    x -= DIST;
252
    z -= DIST;
253

    
254
    for(int i=0; i<layers-1; i++, index++)
255
      {
256
      z += 2*DIST;
257
      centers[index][0] = x;
258
      centers[index][1] = height;
259
      centers[index][2] = z;
260
      }
261

    
262
    x -= DIST;
263
    z += DIST;
264

    
265
    for(int i=0; i<layers-1; i++, index++)
266
      {
267
      x += 2*DIST;
268
      centers[index][0] = x;
269
      centers[index][1] = height;
270
      centers[index][2] = z;
271
      }
272

    
273
    return index;
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

    
278
  float[][] getCubitPositions(int layers)
279
    {
280
    int numO = getNumOctahedrons(layers);
281
    int numT = getNumTetrahedrons(layers);
282
    int index = 0;
283
    float height = 0.0f;
284

    
285
    float[][] CENTERS = new float[numO+numT][3];
286

    
287
    index = createOctahedrons(CENTERS,index,layers,height);
288

    
289
    for(int i=layers-1; i>0; i--)
290
      {
291
      height += SQ2*DIST;
292
      index = createOctahedrons(CENTERS,index,i,+height);
293
      index = createOctahedrons(CENTERS,index,i,-height);
294
      }
295

    
296
    height = DIST*SQ2/2;
297

    
298
    for(int i=layers; i>1; i--)
299
      {
300
      index = createTetrahedrons(CENTERS,index,i,+height);
301
      index = createTetrahedrons(CENTERS,index,i,-height);
302
      height += SQ2*DIST;
303
      }
304

    
305
    return CENTERS;
306
    }
307

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309
// TODO:
310

    
311
  private int retFaceTetraBelongsTo(int tetra, int numLayers)
312
    {
313
    switch(tetra)
314
      {
315
      case 0 : return 1;
316
      case 1 : return 2;
317
      case 2 : return 3;
318
      case 3 : return 0;
319
      case 4 : return 5;
320
      case 5 : return 6;
321
      case 6 : return 7;
322
      case 7 : return 4;
323
      default: return 8;
324
      }
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

    
329
  private Static4D getQuat(int cubit, int numLayers, int numO)
330
    {
331
    if( cubit<numO ) return QUATS[0];
332

    
333
    switch( retFaceTetraBelongsTo(cubit-numO, numLayers) )
334
      {
335
      case 0: return QUATS[0];                          // unit quat
336
      case 1: return new Static4D(0,-SQ2/2,0,SQ2/2);    //  90 along Y
337
      case 2: return QUATS[1];                          // 180 along Y
338
      case 3: return new Static4D(0,+SQ2/2,0,SQ2/2);    //  90 along
339
      case 4: return new Static4D(0,     0,1,    0);    // 180 along Z
340
      case 5: return new Static4D(SQ2/2, 0,SQ2/2,0);    //
341
      case 6: return new Static4D(     1,0,0,    0);    // 180 along X
342
      case 7: return new Static4D(-SQ2/2,0,SQ2/2,0);    //
343
      }
344

    
345
    return null;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
  MeshBase createCubitMesh(int cubit, int numLayers)
351
    {
352
    MeshBase mesh;
353
    int numO = getNumOctahedrons(numLayers);
354

    
355
    if( cubit<numO )
356
      {
357
      if( mOctaMesh==null ) mOctaMesh = FactoryCubit.getInstance().createOctaMesh();
358
      mesh = mOctaMesh.copy(true);
359
      }
360
    else
361
      {
362
      if( mTetraMesh==null ) mTetraMesh = FactoryCubit.getInstance().createTetraMesh();
363
      mesh = mTetraMesh.copy(true);
364
      }
365

    
366
    Static4D sQ = getQuat(cubit,numLayers,numO);
367
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( sQ, new Static3D(0,0,0) );
368
    mesh.apply(quat,0xffffffff,0);
369

    
370
    return mesh;
371
    }
372

    
373
///////////////////////////////////////////////////////////////////////////////////////////////////
374

    
375
  int getFaceColor(int cubit, int cubitface, int size)
376
    {
377
    int numO = getNumOctahedrons(size);
378

    
379
    if( cubit<numO )
380
      {
381
      int axis = 0;
382
      int layer= 1;
383

    
384
      switch(cubitface)
385
        {
386
        case 0: axis = 2; layer =             1; break;
387
        case 1: axis = 0; layer = (1<<(size-1)); break;
388
        case 2: axis = 3; layer =             1; break;
389
        case 3: axis = 1; layer = (1<<(size-1)); break;
390
        case 4: axis = 3; layer = (1<<(size-1)); break;
391
        case 5: axis = 1; layer =             1; break;
392
        case 6: axis = 2; layer = (1<<(size-1)); break;
393
        case 7: axis = 0; layer =             1; break;
394
        }
395

    
396
      return CUBITS[cubit].mRotationRow[axis] == layer ? cubitface : NUM_FACES;
397
      }
398
    else
399
      {
400
      return cubitface>0 ? NUM_FACES : retFaceTetraBelongsTo(cubit-numO, size);
401
      }
402
    }
403

    
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405

    
406
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
407
    {
408
    float E = 0.75f;
409
    float F = 0.50f;
410
    float R = 0.06f;
411
    float S = 0.07f;
412
    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
413

    
414
    FactorySticker factory = FactorySticker.getInstance();
415
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
416
    }
417

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419

    
420
  float returnMultiplier()
421
    {
422
    return 1.5f;
423
    }
424

    
425
///////////////////////////////////////////////////////////////////////////////////////////////////
426

    
427
  float[] getRowChances(int numLayers)
428
    {
429
    float[] chances = new float[numLayers];
430

    
431
    for(int i=0; i<numLayers; i++) chances[i] = ((float)(i+1))/numLayers;
432

    
433
    return chances;
434
    }
435

    
436
///////////////////////////////////////////////////////////////////////////////////////////////////
437
// PUBLIC API
438

    
439
  public Static3D[] getRotationAxis()
440
    {
441
    return ROT_AXIS;
442
    }
443

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445

    
446
  public int getBasicAngle()
447
    {
448
    return 3;
449
    }
450

    
451
///////////////////////////////////////////////////////////////////////////////////////////////////
452

    
453
  public void randomizeNewScramble(int[][] scramble, Random rnd, int num)
454
    {
455
    if( num==0 )
456
      {
457
      scramble[num][0] = rnd.nextInt(ROTATION_AXIS.length);
458
      }
459
    else
460
      {
461
      int newVector = rnd.nextInt(ROTATION_AXIS.length-1);
462
      scramble[num][0] = (newVector>=scramble[num-1][0] ? newVector+1 : newVector);
463
      }
464

    
465
    float rowFloat = rnd.nextFloat();
466

    
467
    for(int row=0; row<mRowChances.length; row++)
468
      {
469
      if( rowFloat<=mRowChances[row] )
470
        {
471
        scramble[num][1] = row;
472
        break;
473
        }
474
      }
475

    
476
    switch( rnd.nextInt(2) )
477
      {
478
      case 0: scramble[num][2] = -1; break;
479
      case 1: scramble[num][2] =  1; break;
480
      }
481
    }
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484

    
485
  int mulQuat(int q1, int q2)
486
    {
487
    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
488

    
489
    float rX = result.get0();
490
    float rY = result.get1();
491
    float rZ = result.get2();
492
    float rW = result.get3();
493

    
494
    final float MAX_ERROR = 0.1f;
495
    float dX,dY,dZ,dW;
496

    
497
    for(int i=0; i<QUATS.length; i++)
498
      {
499
      dX = QUATS[i].get0() - rX;
500
      dY = QUATS[i].get1() - rY;
501
      dZ = QUATS[i].get2() - rZ;
502
      dW = QUATS[i].get3() - rW;
503

    
504
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
505
          dY<MAX_ERROR && dY>-MAX_ERROR &&
506
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
507
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
508

    
509
      dX = QUATS[i].get0() + rX;
510
      dY = QUATS[i].get1() + rY;
511
      dZ = QUATS[i].get2() + rZ;
512
      dW = QUATS[i].get3() + rW;
513

    
514
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
515
          dY<MAX_ERROR && dY>-MAX_ERROR &&
516
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
517
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
518
      }
519

    
520
    return -1;
521
    }
522

    
523
///////////////////////////////////////////////////////////////////////////////////////////////////
524
// The (2 and 3-layered) Diamond is solved if and only if:
525
//
526
// 1) all octahedrons are rotated with the same quat
527
// 2) all tetrahedrons might be also optionally rotated by a 'face neutral' pair of quats
528
//    (indexes of those are kept in the 'mFaceNeutralQuattIndex' table)
529
//
530
// Note: this doesn't work for numLayers > 3, because then we have some 'internal' octahedrons,
531
// which also might be rotate by 'face neutral' pair of quats. We don't care about this (yet?)
532

    
533
  public boolean isSolved()
534
    {
535
    int q = CUBITS[0].mQuatIndex;
536
    int layers = getNumLayers();
537
    int numO = getNumOctahedrons(layers);
538

    
539
    for(int i=1; i<numO; i++)
540
      {
541
      if( CUBITS[i].mQuatIndex != q ) return false;
542
      }
543

    
544
    int qI, q1Index, q2Index, face;
545

    
546
    for(int i=numO; i<NUM_CUBITS; i++)
547
      {
548
      face    = retFaceTetraBelongsTo(i-numO,layers);
549
      q1Index = mFaceNeutralQuatIndex[face][0];
550
      q2Index = mFaceNeutralQuatIndex[face][1];
551
      qI      = CUBITS[i].mQuatIndex;
552

    
553
      if(  qI != q && qI != mulQuat(q,q1Index) && qI != mulQuat(q,q2Index) ) return false;
554
      }
555

    
556
    return true;
557
    }
558

    
559
///////////////////////////////////////////////////////////////////////////////////////////////////
560
// only needed for solvers - there are no Diamond solvers ATM
561

    
562
  public String retObjectString()
563
    {
564
    return "";
565
    }
566

    
567
///////////////////////////////////////////////////////////////////////////////////////////////////
568

    
569
  public int getObjectName(int numLayers)
570
    {
571
    switch(numLayers)
572
      {
573
      case 2: return R.string.diam2;
574
      case 3: return R.string.diam3;
575
      }
576

    
577
    return 0;
578
    }
579

    
580
///////////////////////////////////////////////////////////////////////////////////////////////////
581

    
582
  public int getInventor(int numLayers)
583
    {
584
    switch(numLayers)
585
      {
586
      case 2: return R.string.diam2_inventor;
587
      case 3: return R.string.diam3_inventor;
588
      }
589

    
590
    return 0;
591
    }
592

    
593
///////////////////////////////////////////////////////////////////////////////////////////////////
594

    
595
  public int getComplexity(int numLayers)
596
    {
597
    switch(numLayers)
598
      {
599
      case 2: return 5;
600
      case 3: return 7;
601
      }
602

    
603
    return 0;
604
    }
605
}
(22-22/35)