Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyDiamond.java @ 668423be

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

    
24
import org.distorted.helpers.ObjectShape;
25
import org.distorted.helpers.ObjectSticker;
26
import org.distorted.library.main.DistortedEffects;
27
import org.distorted.library.main.DistortedTexture;
28
import org.distorted.library.mesh.MeshSquare;
29
import org.distorted.library.type.Static3D;
30
import org.distorted.library.type.Static4D;
31
import org.distorted.main.R;
32

    
33
import java.util.Random;
34

    
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36

    
37
public class TwistyDiamond extends TwistyObject
38
{
39
  private static final int FACES_PER_CUBIT =8;
40

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

    
50
  private static final int[] BASIC_ANGLE = new int[] { 3,3,3,3 };
51

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

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

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

    
78
  private static final float DIST = 0.50f;
79

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

    
92
  private static final int[] mTetraToFaceMap = new int[] {1,2,3,0,5,6,7,4};
93

    
94
  private static final double[][] VERTICES_TETRA = new double[][]
95
          {
96
             {-0.5, SQ2/4, 0.0},
97
             { 0.5, SQ2/4, 0.0},
98
             { 0.0,-SQ2/4, 0.5},
99
             { 0.0,-SQ2/4,-0.5}
100
          };
101

    
102
  private static final int[][] VERT_INDEXES_TETRA = new int[][]
103
          {
104
             {2,1,0},   // counterclockwise!
105
             {2,3,1},
106
             {3,2,0},
107
             {3,0,1}
108
          };
109

    
110
  private static final double[][] VERTICES_OCTA = new double[][]
111
          {
112
             { 0.5,   0.0, 0.5},
113
             { 0.5,   0.0,-0.5},
114
             {-0.5,   0.0,-0.5},
115
             {-0.5,   0.0, 0.5},
116
             { 0.0, SQ2/2, 0.0},
117
             { 0.0,-SQ2/2, 0.0}
118
          };
119

    
120
  private static final int[][] VERT_INDEXES_OCTA = new int[][]
121
          {
122
             {3,0,4},   // counterclockwise!
123
             {0,1,4},
124
             {1,2,4},
125
             {2,3,4},
126
             {5,0,3},
127
             {5,1,0},
128
             {5,2,1},
129
             {5,3,2}
130
          };
131

    
132
  private static final float[][] STICKERS = new float[][]
133
          {
134
             { -0.4330127f, -0.25f, 0.4330127f, -0.25f, 0.0f, 0.5f }
135
          };
136

    
137
  private static final ObjectSticker[] mStickers;
138

    
139
  static
140
    {
141
    float radius = 0.06f;
142
    float stroke = 0.07f;
143
    float[] radii = new float[] {radius,radius,radius};
144
    mStickers = new ObjectSticker[STICKERS.length];
145
    mStickers[0] = new ObjectSticker(STICKERS[0],null,radii,stroke);
146
    }
147

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

    
150
  TwistyDiamond(int size, Static4D quat, DistortedTexture texture,
151
                MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
152
    {
153
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.DIAM, res, scrWidth);
154
    }
155

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

    
158
  float getScreenRatio()
159
    {
160
    return 0.65f;
161
    }
162

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

    
165
  Static4D[] getQuats()
166
    {
167
    return QUATS;
168
    }
169

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

    
172
  int getNumFaces()
173
    {
174
    return FACE_COLORS.length;
175
    }
176

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

    
179
  boolean shouldResetTextureMaps()
180
    {
181
    return false;
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

    
186
  int getNumStickerTypes(int numLayers)
187
    {
188
    return STICKERS.length;
189
    }
190

    
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192

    
193
  float[][] getCuts(int numLayers)
194
    {
195
    if( numLayers<2 )
196
      {
197
      return null;
198
      }
199
    else
200
      {
201
      float[][] cuts = new float[4][numLayers-1];
202
      float dist = SQ6*0.666f*DIST;
203
      float cut  = 0.5f*dist*(2-numLayers);
204

    
205
      for(int i=0; i<numLayers-1; i++)
206
        {
207
        cuts[0][i] = cut;
208
        cuts[1][i] = cut;
209
        cuts[2][i] = cut;
210
        cuts[3][i] = cut;
211
        cut += dist;
212
        }
213

    
214
      return cuts;
215
      }
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

    
220
  int getNumCubitFaces()
221
    {
222
    return FACES_PER_CUBIT;
223
    }
224

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

    
227
  private int getNumOctahedrons(int layers)
228
    {
229
    return layers==1 ? 1 : 4*(layers-1)*(layers-1) + 2;
230
    }
231

    
232
///////////////////////////////////////////////////////////////////////////////////////////////////
233

    
234
  private int getNumTetrahedrons(int layers)
235
    {
236
    return 4*layers*(layers-1);
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

    
241
  private int createOctaPositions(float[][] centers, int index, int layers, float height)
242
    {
243
    float x = DIST*(layers-1);
244
    float z = DIST*(layers+1);
245

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

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

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

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

    
278
    return index;
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

    
283
  private int createTetraPositions(float[][] centers, int index, int layers, float height)
284
    {
285
    float x = DIST*(layers-1);
286
    float z = DIST*layers;
287

    
288
    for(int i=0; i<layers-1; i++, index++)
289
      {
290
      z -= 2*DIST;
291
      centers[index][0] = x;
292
      centers[index][1] = height;
293
      centers[index][2] = z;
294
      }
295

    
296
    x += DIST;
297
    z -= DIST;
298

    
299
    for(int i=0; i<layers-1; i++, index++)
300
      {
301
      x -= 2*DIST;
302
      centers[index][0] = x;
303
      centers[index][1] = height;
304
      centers[index][2] = z;
305
      }
306

    
307
    x -= DIST;
308
    z -= DIST;
309

    
310
    for(int i=0; i<layers-1; i++, index++)
311
      {
312
      z += 2*DIST;
313
      centers[index][0] = x;
314
      centers[index][1] = height;
315
      centers[index][2] = z;
316
      }
317

    
318
    x -= DIST;
319
    z += DIST;
320

    
321
    for(int i=0; i<layers-1; i++, index++)
322
      {
323
      x += 2*DIST;
324
      centers[index][0] = x;
325
      centers[index][1] = height;
326
      centers[index][2] = z;
327
      }
328

    
329
    return index;
330
    }
331

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

    
334
  float[][] getCubitPositions(int layers)
335
    {
336
    int numO = getNumOctahedrons(layers);
337
    int numT = getNumTetrahedrons(layers);
338
    int index = 0;
339
    float height = 0.0f;
340

    
341
    float[][] CENTERS = new float[numO+numT][3];
342

    
343
    index = createOctaPositions(CENTERS,index,layers,height);
344

    
345
    for(int i=layers-1; i>0; i--)
346
      {
347
      height += SQ2*DIST;
348
      index = createOctaPositions(CENTERS,index,i,+height);
349
      index = createOctaPositions(CENTERS,index,i,-height);
350
      }
351

    
352
    height = DIST*SQ2/2;
353

    
354
    for(int i=layers; i>1; i--)
355
      {
356
      index = createTetraPositions(CENTERS,index,i,+height);
357
      index = createTetraPositions(CENTERS,index,i,-height);
358
      height += SQ2*DIST;
359
      }
360

    
361
    return CENTERS;
362
    }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365

    
366
  private int retFaceTetraBelongsTo(int tetra, int numLayers)
367
    {
368
    for(int i=numLayers-1; i>0; i--)
369
      {
370
      if( tetra < 8*i ) return mTetraToFaceMap[tetra/i];
371
      tetra -= 8*i;
372
      }
373

    
374
    return -1;
375
    }
376

    
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378

    
379
  ObjectShape getObjectShape(int cubit, int numLayers)
380
    {
381
    int variant = getCubitVariant(cubit,numLayers);
382
    int N = numLayers>3 ? 5:6;
383
    int E = numLayers>2 ? (numLayers>3 ? 0:1):2;
384

    
385
    if( variant==0 )
386
      {
387
      float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,N,E,E} };
388
      int[] bandIndices   = new int[] { 0,0,0,0,0,0,0,0 };
389
      float[][] corners   = new float[][] { {0.04f,0.20f} };
390
      int[] cornerIndices = new int[] { 0,0,0,0,0,0 };
391
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
392
      int[] centerIndices = new int[] { 0,0,0,0,0,0 };
393
      return new ObjectShape(VERTICES_OCTA,VERT_INDEXES_OCTA,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
394
      }
395
    else
396
      {
397
      float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,N,E,E} };
398
      int[] bandIndices   = new int[] { 0,0,0,0 };
399
      float[][] corners   = new float[][] { {0.08f,0.15f} };
400
      int[] cornerIndices = new int[] { 0,0,0,0 };
401
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
402
      int[] centerIndices = new int[] { 0,0,0,0 };
403
      return new ObjectShape(VERTICES_TETRA,VERT_INDEXES_TETRA,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
404
      }
405
    }
406

    
407
///////////////////////////////////////////////////////////////////////////////////////////////////
408

    
409
  Static4D getQuat(int cubit, int numLayers)
410
    {
411
    int numO = getNumOctahedrons(numLayers);
412

    
413
    if( cubit<numO ) return QUATS[0];
414

    
415
    switch( retFaceTetraBelongsTo(cubit-numO, numLayers) )
416
      {
417
      case 0: return QUATS[0];                          // unit quat
418
      case 1: return new Static4D(0,-SQ2/2,0,SQ2/2);    //  90 along Y
419
      case 2: return QUATS[1];                          // 180 along Y
420
      case 3: return new Static4D(0,+SQ2/2,0,SQ2/2);    //  90 along
421
      case 4: return new Static4D(0,     0,1,    0);    // 180 along Z
422
      case 5: return new Static4D(SQ2/2, 0,SQ2/2,0);    //
423
      case 6: return new Static4D(     1,0,0,    0);    // 180 along X
424
      case 7: return new Static4D(-SQ2/2,0,SQ2/2,0);    //
425
      }
426

    
427
    return null;
428
    }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431

    
432
  int getNumCubitVariants(int numLayers)
433
    {
434
    return 2;
435
    }
436

    
437
///////////////////////////////////////////////////////////////////////////////////////////////////
438

    
439
  int getCubitVariant(int cubit, int numLayers)
440
    {
441
    return cubit<getNumOctahedrons(numLayers) ? 0 : 1;
442
    }
443

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

    
446
  int getFaceColor(int cubit, int cubitface, int size)
447
    {
448
    int numO = getNumOctahedrons(size);
449

    
450
    if( cubit<numO )
451
      {
452
      int axis = 0;
453
      int layer= 1;
454

    
455
      switch(cubitface)
456
        {
457
        case 0: axis = 2; layer =             1; break;
458
        case 1: axis = 0; layer = (1<<(size-1)); break;
459
        case 2: axis = 3; layer =             1; break;
460
        case 3: axis = 1; layer = (1<<(size-1)); break;
461
        case 4: axis = 3; layer = (1<<(size-1)); break;
462
        case 5: axis = 1; layer =             1; break;
463
        case 6: axis = 2; layer = (1<<(size-1)); break;
464
        case 7: axis = 0; layer =             1; break;
465
        }
466

    
467
      return CUBITS[cubit].mRotationRow[axis] == layer ? cubitface : NUM_TEXTURES;
468
      }
469
    else
470
      {
471
      return cubitface>0 ? NUM_TEXTURES : retFaceTetraBelongsTo(cubit-numO, size);
472
      }
473
    }
474

    
475
///////////////////////////////////////////////////////////////////////////////////////////////////
476

    
477
  int getColor(int face)
478
    {
479
    return FACE_COLORS[face];
480
    }
481

    
482
///////////////////////////////////////////////////////////////////////////////////////////////////
483

    
484
  ObjectSticker retSticker(int face)
485
    {
486
    return mStickers[face/NUM_FACES];
487
    }
488

    
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490

    
491
  float returnMultiplier()
492
    {
493
    return 1.5f;
494
    }
495

    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497
// PUBLIC API
498

    
499
  public Static3D[] getRotationAxis()
500
    {
501
    return ROT_AXIS;
502
    }
503

    
504
///////////////////////////////////////////////////////////////////////////////////////////////////
505

    
506
  public int[] getBasicAngle()
507
    {
508
    return BASIC_ANGLE;
509
    }
510

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

    
513
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
514
    {
515
    if( curr==0 )
516
      {
517
      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
518
      }
519
    else
520
      {
521
      int newVector = rnd.nextInt(NUM_AXIS -1);
522
      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
523
      }
524

    
525
    float rowFloat = rnd.nextFloat();
526
    int numLayers = getNumLayers();
527

    
528
    for(int row=0; row<numLayers; row++)
529
      {
530
      if( rowFloat*numLayers <= row+1 )
531
        {
532
        scramble[curr][1] = row;
533
        break;
534
        }
535
      }
536

    
537
    switch( rnd.nextInt(2) )
538
      {
539
      case 0: scramble[curr][2] = -1; break;
540
      case 1: scramble[curr][2] =  1; break;
541
      }
542
    }
543

    
544
///////////////////////////////////////////////////////////////////////////////////////////////////
545
// The Diamond is solved if and only if:
546
//
547
// - all cubits are rotated by the same quat
548
// - those which are internal to the side, i.e. those which have only one 'non-black' face, might
549
//   also be optionally rotated by one of the two quats whose axis is perpendicular to the face.
550
//   (including some octahedrons if numLayers>=4)
551

    
552
  public boolean isSolved()
553
    {
554
    int q = CUBITS[0].mQuatIndex;
555
    int layers = getNumLayers();
556
    int numO = getNumOctahedrons(layers);
557

    
558
    for(int i=1; i<numO; i++)
559
      {
560
      if( CUBITS[i].mQuatIndex != q ) return false;
561
      }
562

    
563
    int qI, q1Index, q2Index, face;
564

    
565
    for(int i=numO; i<NUM_CUBITS; i++)
566
      {
567
      face    = retFaceTetraBelongsTo(i-numO,layers);
568
      q1Index = mFaceNeutralQuatIndex[face][0];
569
      q2Index = mFaceNeutralQuatIndex[face][1];
570
      qI      = CUBITS[i].mQuatIndex;
571

    
572
      if( qI != q && qI != mulQuat(q,q1Index) && qI != mulQuat(q,q2Index) ) return false;
573
      }
574

    
575
    return true;
576
    }
577

    
578
///////////////////////////////////////////////////////////////////////////////////////////////////
579

    
580
  public int getObjectName(int numLayers)
581
    {
582
    switch(numLayers)
583
      {
584
      case 2: return R.string.diam2;
585
      case 3: return R.string.diam3;
586
      case 4: return R.string.diam4;
587
      }
588

    
589
    return 0;
590
    }
591

    
592
///////////////////////////////////////////////////////////////////////////////////////////////////
593

    
594
  public int getInventor(int numLayers)
595
    {
596
    switch(numLayers)
597
      {
598
      case 2: return R.string.diam2_inventor;
599
      case 3: return R.string.diam3_inventor;
600
      case 4: return R.string.diam4_inventor;
601
      }
602

    
603
    return 0;
604
    }
605

    
606
///////////////////////////////////////////////////////////////////////////////////////////////////
607

    
608
  public int getComplexity(int numLayers)
609
    {
610
    switch(numLayers)
611
      {
612
      case 2: return 4;
613
      case 3: return 6;
614
      case 4: return 8;
615
      }
616

    
617
    return 0;
618
    }
619
}
(23-23/41)