Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyDiamond.java @ a4962b9c

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 final int[] mTetraToFaceMap = new int[] {1,2,3,0,5,6,7,4};
94

    
95
  private static MeshBase mOctaMesh, mTetraMesh;
96

    
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98

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

    
105
///////////////////////////////////////////////////////////////////////////////////////////////////
106

    
107
  float getScreenRatio()
108
    {
109
    return 0.65f;
110
    }
111

    
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113

    
114
  Static4D[] getQuats()
115
    {
116
    return QUATS;
117
    }
118

    
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

    
121
  int getNumFaces()
122
    {
123
    return FACE_COLORS.length;
124
    }
125

    
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127

    
128
  boolean shouldResetTextureMaps()
129
    {
130
    return false;
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

    
135
  int getNumStickerTypes(int numLayers)
136
    {
137
    return 1;
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141

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

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

    
160
      return cuts;
161
      }
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165

    
166
  int getNumCubitFaces()
167
    {
168
    return FACES_PER_CUBIT;
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172

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

    
178
///////////////////////////////////////////////////////////////////////////////////////////////////
179

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

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

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

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

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

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

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

    
224
    return index;
225
    }
226

    
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

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

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

    
242
    x += DIST;
243
    z -= DIST;
244

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

    
253
    x -= DIST;
254
    z -= DIST;
255

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

    
264
    x -= DIST;
265
    z += DIST;
266

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

    
275
    return index;
276
    }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279

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

    
287
    float[][] CENTERS = new float[numO+numT][3];
288

    
289
    index = createOctahedrons(CENTERS,index,layers,height);
290

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

    
298
    height = DIST*SQ2/2;
299

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

    
307
    return CENTERS;
308
    }
309

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

    
312
  private int retFaceTetraBelongsTo(int tetra, int numLayers)
313
    {
314
    for(int i=numLayers-1; i>0; i--)
315
      {
316
      if( tetra < 8*i ) return mTetraToFaceMap[tetra/i];
317
      tetra -= 8*i;
318
      }
319

    
320
    return -1;
321
    }
322

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324

    
325
  private Static4D getQuat(int cubit, int numLayers, int numO)
326
    {
327
    if( cubit<numO ) return QUATS[0];
328

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

    
341
    return null;
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345

    
346
  MeshBase createCubitMesh(int cubit, int numLayers)
347
    {
348
    MeshBase mesh;
349
    int numO = getNumOctahedrons(numLayers);
350

    
351
    if( cubit<numO )
352
      {
353
      if( mOctaMesh==null ) mOctaMesh = FactoryCubit.getInstance().createOctaMesh();
354
      mesh = mOctaMesh.copy(true);
355
      }
356
    else
357
      {
358
      if( mTetraMesh==null ) mTetraMesh = FactoryCubit.getInstance().createTetraMesh();
359
      mesh = mTetraMesh.copy(true);
360
      }
361

    
362
    Static4D sQ = getQuat(cubit,numLayers,numO);
363
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( sQ, new Static3D(0,0,0) );
364
    mesh.apply(quat,0xffffffff,0);
365

    
366
    return mesh;
367
    }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370

    
371
  int getFaceColor(int cubit, int cubitface, int size)
372
    {
373
    int numO = getNumOctahedrons(size);
374

    
375
    if( cubit<numO )
376
      {
377
      int axis = 0;
378
      int layer= 1;
379

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

    
392
      return CUBITS[cubit].mRotationRow[axis] == layer ? cubitface : NUM_TEXTURES;
393
      }
394
    else
395
      {
396
      return cubitface>0 ? NUM_TEXTURES : retFaceTetraBelongsTo(cubit-numO, size);
397
      }
398
    }
399

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401

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

    
410
    FactorySticker factory = FactorySticker.getInstance();
411
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
412
    }
413

    
414
///////////////////////////////////////////////////////////////////////////////////////////////////
415

    
416
  float returnMultiplier()
417
    {
418
    return 1.5f;
419
    }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422

    
423
  float[] getRowChances(int numLayers)
424
    {
425
    float[] chances = new float[numLayers];
426

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

    
429
    return chances;
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433
// PUBLIC API
434

    
435
  public Static3D[] getRotationAxis()
436
    {
437
    return ROT_AXIS;
438
    }
439

    
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

    
442
  public int getBasicAngle()
443
    {
444
    return 3;
445
    }
446

    
447
///////////////////////////////////////////////////////////////////////////////////////////////////
448

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

    
461
    float rowFloat = rnd.nextFloat();
462

    
463
    for(int row=0; row<mRowChances.length; row++)
464
      {
465
      if( rowFloat<=mRowChances[row] )
466
        {
467
        scramble[num][1] = row;
468
        break;
469
        }
470
      }
471

    
472
    switch( rnd.nextInt(2) )
473
      {
474
      case 0: scramble[num][2] = -1; break;
475
      case 1: scramble[num][2] =  1; break;
476
      }
477
    }
478

    
479
///////////////////////////////////////////////////////////////////////////////////////////////////
480

    
481
  int mulQuat(int q1, int q2)
482
    {
483
    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
484

    
485
    float rX = result.get0();
486
    float rY = result.get1();
487
    float rZ = result.get2();
488
    float rW = result.get3();
489

    
490
    final float MAX_ERROR = 0.1f;
491
    float dX,dY,dZ,dW;
492

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

    
500
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
501
          dY<MAX_ERROR && dY>-MAX_ERROR &&
502
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
503
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
504

    
505
      dX = QUATS[i].get0() + rX;
506
      dY = QUATS[i].get1() + rY;
507
      dZ = QUATS[i].get2() + rZ;
508
      dW = QUATS[i].get3() + rW;
509

    
510
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
511
          dY<MAX_ERROR && dY>-MAX_ERROR &&
512
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
513
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
514
      }
515

    
516
    return -1;
517
    }
518

    
519
///////////////////////////////////////////////////////////////////////////////////////////////////
520
// The Diamond is solved if and only if:
521
//
522
// 1) all octahedrons are rotated with the same quat
523
// 2) all tetrahedrons might be also optionally rotated by a 'face neutral' pair of quats
524
//    (indexes of those are kept in the 'mFaceNeutralQuattIndex' table)
525
//
526
// Note: this works for any size, because even if layers>3 - and then there are 'face-internal'
527
// octahedrons which, it would seem, can be rotated by those 'face neutral' pairs of quats - but
528
// in reality no, because if they were, the octahedrons would then not fit in the lattice...
529

    
530
  public boolean isSolved()
531
    {
532
    int q = CUBITS[0].mQuatIndex;
533
    int layers = getNumLayers();
534
    int numO = getNumOctahedrons(layers);
535

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

    
541
    int qI, q1Index, q2Index, face;
542

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

    
550
      if(  qI != q && qI != mulQuat(q,q1Index) && qI != mulQuat(q,q2Index) ) return false;
551
      }
552

    
553
    return true;
554
    }
555

    
556
///////////////////////////////////////////////////////////////////////////////////////////////////
557
// only needed for solvers - there are no Diamond solvers ATM
558

    
559
  public String retObjectString()
560
    {
561
    return "";
562
    }
563

    
564
///////////////////////////////////////////////////////////////////////////////////////////////////
565

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

    
574
    return 0;
575
    }
576

    
577
///////////////////////////////////////////////////////////////////////////////////////////////////
578

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

    
587
    return 0;
588
    }
589

    
590
///////////////////////////////////////////////////////////////////////////////////////////////////
591

    
592
  public int getComplexity(int numLayers)
593
    {
594
    switch(numLayers)
595
      {
596
      case 2: return 5;
597
      case 3: return 7;
598
      }
599

    
600
    return 0;
601
    }
602
}
(22-22/35)