Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyDiamond.java @ 680f921e

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_GREY  , COLOR_VIOLET,
56
           COLOR_YELLOW, COLOR_WHITE ,
57
           COLOR_BLUE  , COLOR_RED   ,
58
           COLOR_ORANGE, COLOR_GREEN
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
  // Colors of the faces of cubits. Each cubit has 8 faces
82
  private static final int[][] mOctaFaceMap = new int[][]
83
         {
84
           { 6,1,8,8, 2,5,8,8 },
85
           { 8,1,3,8, 8,5,7,8 },
86
           { 8,8,3,4, 8,8,7,0 },
87
           { 6,8,8,4, 2,8,8,0 },
88
           { 6,1,3,4, 8,8,8,8 },
89
           { 8,8,8,8, 2,5,7,0 },
90
         };
91

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

    
94
  private static MeshBase mOctaMesh, mTetraMesh;
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

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

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

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

    
111
///////////////////////////////////////////////////////////////////////////////////////////////////
112

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

    
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119

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

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

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

    
132
///////////////////////////////////////////////////////////////////////////////////////////////////
133

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

    
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140

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

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

    
159
      return cuts;
160
      }
161
    }
162

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

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

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

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

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

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

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

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

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

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

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

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

    
223
    return index;
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227

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

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

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

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

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

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

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

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

    
274
    return index;
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278

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

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

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

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

    
297
    height = DIST*SQ2/2;
298

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

    
306
    return CENTERS;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310
// TODO:
311

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

    
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329

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

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

    
346
    return null;
347
    }
348

    
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350

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

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

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

    
371
    return mesh;
372
    }
373

    
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375
// TODO
376

    
377
  int getFaceColor(int cubit, int cubitface, int numLayers)
378
    {
379
    int numO = getNumOctahedrons(numLayers);
380

    
381
    if( cubit<numO )
382
      {
383
      return mOctaFaceMap[cubit][cubitface];
384
      }
385
    else
386
      {
387
      return cubitface>0 ? 8 : mTetraFaceMap[retFaceTetraBelongsTo(cubit-numO, numLayers)];
388
      }
389
    }
390

    
391
///////////////////////////////////////////////////////////////////////////////////////////////////
392

    
393
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
394
    {
395
    float E = 0.75f;
396
    float F = 0.50f;
397
    float R = 0.06f;
398
    float S = 0.07f;
399
    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
400

    
401
    FactorySticker factory = FactorySticker.getInstance();
402
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
403
    }
404

    
405
///////////////////////////////////////////////////////////////////////////////////////////////////
406

    
407
  float returnMultiplier()
408
    {
409
    return 1.5f;
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413

    
414
  float[] getRowChances(int numLayers)
415
    {
416
    float[] chances = new float[numLayers];
417

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

    
420
    return chances;
421
    }
422

    
423
///////////////////////////////////////////////////////////////////////////////////////////////////
424
// PUBLIC API
425

    
426
  public Static3D[] getRotationAxis()
427
    {
428
    return ROT_AXIS;
429
    }
430

    
431
///////////////////////////////////////////////////////////////////////////////////////////////////
432

    
433
  public int getBasicAngle()
434
    {
435
    return 3;
436
    }
437

    
438
///////////////////////////////////////////////////////////////////////////////////////////////////
439

    
440
  public void randomizeNewScramble(int[][] scramble, Random rnd, int num)
441
    {
442
    if( num==0 )
443
      {
444
      scramble[num][0] = rnd.nextInt(ROTATION_AXIS.length);
445
      }
446
    else
447
      {
448
      int newVector = rnd.nextInt(ROTATION_AXIS.length-1);
449
      scramble[num][0] = (newVector>=scramble[num-1][0] ? newVector+1 : newVector);
450
      }
451

    
452
    float rowFloat = rnd.nextFloat();
453

    
454
    for(int row=0; row<mRowChances.length; row++)
455
      {
456
      if( rowFloat<=mRowChances[row] )
457
        {
458
        scramble[num][1] = row;
459
        break;
460
        }
461
      }
462

    
463
    switch( rnd.nextInt(2) )
464
      {
465
      case 0: scramble[num][2] = -1; break;
466
      case 1: scramble[num][2] =  1; break;
467
      }
468
    }
469

    
470
///////////////////////////////////////////////////////////////////////////////////////////////////
471

    
472
  int mulQuat(int q1, int q2)
473
    {
474
    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
475

    
476
    float rX = result.get0();
477
    float rY = result.get1();
478
    float rZ = result.get2();
479
    float rW = result.get3();
480

    
481
    final float MAX_ERROR = 0.1f;
482
    float dX,dY,dZ,dW;
483

    
484
    for(int i=0; i<QUATS.length; i++)
485
      {
486
      dX = QUATS[i].get0() - rX;
487
      dY = QUATS[i].get1() - rY;
488
      dZ = QUATS[i].get2() - rZ;
489
      dW = QUATS[i].get3() - rW;
490

    
491
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
492
          dY<MAX_ERROR && dY>-MAX_ERROR &&
493
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
494
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
495

    
496
      dX = QUATS[i].get0() + rX;
497
      dY = QUATS[i].get1() + rY;
498
      dZ = QUATS[i].get2() + rZ;
499
      dW = QUATS[i].get3() + rW;
500

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

    
507
    return -1;
508
    }
509

    
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511
// The Diamond is solved if and only if:
512
//
513
// 1) all 6 octahedrons are rotated with the same quat
514
// 2) the 8 tetrahedrons are rotated with the quat and, optionally, the can also be rotated
515
//    by multitudes of 120 degrees along the face they are the center of.
516
//
517
// so:
518
// 1) cubits 6,12: can also be QUAT 6,10
519
// 2) cubits 7,13: can also be QUAT 4,8
520
// 3) cubits 8,10: can also be QUAT 7,11
521
// 4) cubits 9,11: can also be QUAT 5,9
522
// TODO
523

    
524
  public boolean isSolved()
525
    {
526
    int q = CUBITS[0].mQuatIndex;
527

    
528
    if ( CUBITS[ 1].mQuatIndex == q &&
529
         CUBITS[ 2].mQuatIndex == q &&
530
         CUBITS[ 3].mQuatIndex == q &&
531
         CUBITS[ 4].mQuatIndex == q &&
532
         CUBITS[ 5].mQuatIndex == q  )
533
      {
534
      int q1 = mulQuat(q,5);
535
      int q2 = mulQuat(q,9);
536

    
537
      if( CUBITS[ 9].mQuatIndex != q && CUBITS[ 9].mQuatIndex != q1 && CUBITS[ 9].mQuatIndex != q2 ) return false;
538
      if( CUBITS[11].mQuatIndex != q && CUBITS[11].mQuatIndex != q1 && CUBITS[11].mQuatIndex != q2 ) return false;
539

    
540
      q1 = mulQuat(q,4);
541
      q2 = mulQuat(q,8);
542

    
543
      if( CUBITS[ 7].mQuatIndex != q && CUBITS[ 7].mQuatIndex != q1 && CUBITS[ 7].mQuatIndex != q2 ) return false;
544
      if( CUBITS[13].mQuatIndex != q && CUBITS[13].mQuatIndex != q1 && CUBITS[13].mQuatIndex != q2 ) return false;
545

    
546
      q1 = mulQuat(q,6);
547
      q2 = mulQuat(q,10);
548

    
549
      if( CUBITS[ 6].mQuatIndex != q && CUBITS[ 6].mQuatIndex != q1 && CUBITS[ 6].mQuatIndex != q2 ) return false;
550
      if( CUBITS[12].mQuatIndex != q && CUBITS[12].mQuatIndex != q1 && CUBITS[12].mQuatIndex != q2 ) return false;
551

    
552
      q1 = mulQuat(q,7);
553
      q2 = mulQuat(q,11);
554

    
555
      if( CUBITS[ 8].mQuatIndex != q && CUBITS[ 8].mQuatIndex != q1 && CUBITS[ 8].mQuatIndex != q2 ) return false;
556
      if( CUBITS[10].mQuatIndex != q && CUBITS[10].mQuatIndex != q1 && CUBITS[10].mQuatIndex != q2 ) return false;
557

    
558
      return true;
559
      }
560

    
561
    return false;
562
    }
563

    
564
///////////////////////////////////////////////////////////////////////////////////////////////////
565
// only needed for solvers - there are no Diamond solvers ATM
566

    
567
  public String retObjectString()
568
    {
569
    return "";
570
    }
571

    
572
///////////////////////////////////////////////////////////////////////////////////////////////////
573

    
574
  public int getObjectName(int numLayers)
575
    {
576
    return R.string.diam2;
577
    }
578

    
579
///////////////////////////////////////////////////////////////////////////////////////////////////
580

    
581
  public int getInventor(int numLayers)
582
    {
583
    return R.string.diam2_inventor;
584
    }
585

    
586
///////////////////////////////////////////////////////////////////////////////////////////////////
587

    
588
  public int getComplexity(int numLayers)
589
    {
590
    return 5;
591
    }
592
}
(22-22/35)