Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyPyraminx.java @ acf165d9

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 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.FactoryCubit;
25
import org.distorted.helpers.ObjectShape;
26
import org.distorted.helpers.ObjectSticker;
27
import org.distorted.library.effect.MatrixEffectQuaternion;
28
import org.distorted.library.main.DistortedEffects;
29
import org.distorted.library.main.DistortedTexture;
30
import org.distorted.library.mesh.MeshBase;
31
import org.distorted.library.mesh.MeshSquare;
32
import org.distorted.library.type.Static3D;
33
import org.distorted.library.type.Static4D;
34
import org.distorted.main.R;
35

    
36
import java.util.Random;
37

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

    
40
public class TwistyPyraminx extends TwistyObject
41
{
42
  static final float SCREEN_RATIO = 0.88f;
43

    
44
  static final Static3D[] ROT_AXIS = new Static3D[]
45
         {
46
           new Static3D(     0,-SQ3/3,-SQ6/3),
47
           new Static3D(     0,-SQ3/3,+SQ6/3),
48
           new Static3D(+SQ6/3,+SQ3/3,     0),
49
           new Static3D(-SQ6/3,+SQ3/3,     0),
50
         };
51

    
52
  private static final int[] BASIC_ANGLE = new int[] { 3,3,3,3 };
53

    
54
  private static final int[] FACE_COLORS = new int[]
55
         {
56
           COLOR_GREEN , COLOR_YELLOW,
57
           COLOR_BLUE  , COLOR_RED
58
         };
59

    
60
  // computed with res/raw/compute_quats.c
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.5f,   0.0f,  0.5f),
66
           new Static4D(-SQ2/2,   0.5f,   0.0f,  0.5f),
67
           new Static4D(  0.0f,  -0.5f, -SQ2/2,  0.5f),
68
           new Static4D(  0.0f,  -0.5f,  SQ2/2,  0.5f),
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.0f,  SQ2/2,  0.0f),
74
           new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
75
         };
76

    
77
  private static final double[][] VERTICES_TETRA = new double[][]
78
          {
79
             {-0.5, SQ2/4, 0.0},
80
             { 0.5, SQ2/4, 0.0},
81
             { 0.0,-SQ2/4, 0.5},
82
             { 0.0,-SQ2/4,-0.5}
83
          };
84

    
85
  private static final int[][] VERT_INDEXES_TETRA = new int[][]
86
          {
87
             {2,1,0},   // counterclockwise!
88
             {3,0,1},
89
             {3,2,0},
90
             {2,3,1}
91
          };
92

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

    
103
  private static final int[][] VERT_INDEXES_OCTA = new int[][]
104
          {
105
             {3,0,4},   // counterclockwise!
106
             {0,1,4},
107
             {1,2,4},
108
             {2,3,4},
109
             {5,0,3},
110
             {5,1,0},
111
             {5,2,1},
112
             {5,3,2}
113
          };
114

    
115
  private static final float[][] STICKERS = new float[][]
116
          {
117
             { -0.4330127f, -0.25f, 0.4330127f, -0.25f, 0.0f, 0.5f }
118
          };
119

    
120
  private static float[] mRowChances;
121

    
122
  private static final ObjectSticker[] mStickers;
123

    
124
  static
125
    {
126
    mStickers = new ObjectSticker[STICKERS.length];
127
    final float stroke = 0.08f;
128
    final float radius = 0.06f;
129
    final float[] radii= {radius,radius,radius};
130
    mStickers[0] = new ObjectSticker(STICKERS[0],null,radii,stroke);
131
    }
132

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

    
135
  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
136
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
137
    {
138
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
139
    }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  private float[] getRowChances(int numLayers)
144
    {
145
    int total = numLayers*(numLayers+1)/2;
146
    float running=0.0f;
147
    float[] chances = new float[numLayers];
148

    
149
    for(int i=0; i<numLayers; i++)
150
      {
151
      running += (numLayers-i);
152
      chances[i] = running / total;
153
      }
154

    
155
    return chances;
156
    }
157

    
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159

    
160
  private void addTetrahedralLattice(int size, int index, float[][] pos)
161
    {
162
    final float DX = 1.0f;
163
    final float DY = SQ2/2;
164
    final float DZ = 1.0f;
165

    
166
    float startX = 0.0f;
167
    float startY =-DY*(size-1)/2;
168
    float startZ = DZ*(size-1)/2;
169

    
170
    for(int layer=0; layer<size; layer++)
171
      {
172
      float currX = startX;
173
      float currY = startY;
174

    
175
      for(int x=0; x<layer+1; x++)
176
        {
177
        float currZ = startZ;
178

    
179
        for(int z=0; z<size-layer; z++)
180
          {
181
          pos[index] = new float[] {currX,currY,currZ};
182
          index++;
183
          currZ -= DZ;
184
          }
185

    
186
        currX += DX;
187
        }
188

    
189
      startX-=DX/2;
190
      startY+=DY;
191
      startZ-=DZ/2;
192
      }
193
    }
194

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196
// there are (n^3-n)/6 octahedrons and ((n+1)^3 - (n+1))/6 tetrahedrons
197

    
198
  float[][] getCubitPositions(int size)
199
    {
200
    int numOcta = (size-1)*size*(size+1)/6;
201
    int numTetra= size*(size+1)*(size+2)/6;
202
    float[][] ret = new float[numOcta+numTetra][];
203

    
204
    addTetrahedralLattice(size-1,      0,ret);
205
    addTetrahedralLattice(size  ,numOcta,ret);
206

    
207
    return ret;
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  Static4D[] getQuats()
213
    {
214
    return QUATS;
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

    
219
  int getNumFaces()
220
    {
221
    return FACE_COLORS.length;
222
    }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
  int getNumStickerTypes(int numLayers)
227
    {
228
    return STICKERS.length;
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

    
233
  float[][] getCuts(int size)
234
    {
235
    float[][] cuts = new float[4][size-1];
236

    
237
    for(int i=0; i<size-1; i++)
238
      {
239
      float cut = (1.0f-0.25f*size+i)*(SQ6/3);
240
      cuts[0][i] = cut;
241
      cuts[1][i] = cut;
242
      cuts[2][i] = cut;
243
      cuts[3][i] = cut;
244
      }
245

    
246
    return cuts;
247
    }
248

    
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250

    
251
  int getNumCubitFaces()
252
    {
253
    return 8;
254
    }
255

    
256
///////////////////////////////////////////////////////////////////////////////////////////////////
257

    
258
  float getScreenRatio()
259
    {
260
    return SCREEN_RATIO;
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

    
265
  boolean shouldResetTextureMaps()
266
    {
267
    return false;
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  private int getNumOctahedrons(int numLayers)
273
    {
274
    return (numLayers-1)*numLayers*(numLayers+1)/6;
275
    }
276

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

    
279
  private int faceColor(int cubit, int axis)
280
    {
281
    return CUBITS[cubit].mRotationRow[axis] == 1 ? axis : NUM_FACES;
282
    }
283

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285

    
286
  int getFaceColor(int cubit, int cubitface, int size)
287
    {
288
    if( cubit< (size-1)*size*(size+1)/6 )
289
      {
290
      switch( cubitface )
291
        {
292
        case 0: return faceColor(cubit,0);
293
        case 2: return faceColor(cubit,1);
294
        case 5: return faceColor(cubit,3);
295
        case 7: return faceColor(cubit,2);
296
        default:return NUM_FACES;
297
        }
298
      }
299
    else
300
      {
301
      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
302
      }
303
    }
304

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306

    
307
  ObjectShape getObjectShape(int cubit, int numLayers)
308
    {
309
    int variant = getCubitVariant(cubit,numLayers);
310

    
311
    if( variant==0 )
312
      {
313
      int N = numLayers==3? 6 : 5;
314
      int E = numLayers==3? 2 : 1;
315
      float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,N,E,E} };
316
      int[] bandIndices   = new int[] { 0,0,0,0,0,0,0,0 };
317
      float[][] corners   = new float[][] { {0.04f,0.20f} };
318
      int[] cornerIndices = new int[] { 0,0,0,0,0,0 };
319
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
320
      int[] centerIndices = new int[] { 0,0,0,0,0,0 };
321
      return new ObjectShape(VERTICES_OCTA,VERT_INDEXES_OCTA,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
322
      }
323
    else
324
      {
325
      int N = numLayers==3? 6 : 5;
326
      int E = numLayers==3? 2 : 1;
327
      float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,N,E,E} };
328
      int[] bandIndices   = new int[] { 0,0,0,0 };
329
      float[][] corners   = new float[][] { {0.06f,0.15f} };
330
      int[] cornerIndices = new int[] { 0,0,0,0 };
331
      float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
332
      int[] centerIndices = new int[] { 0,0,0,0 };
333
      return new ObjectShape(VERTICES_TETRA,VERT_INDEXES_TETRA,bands,bandIndices,corners,cornerIndices,centers,centerIndices,getNumCubitFaces(), null);
334
      }
335
    }
336

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338

    
339
  private Static4D getQuat(int cubit, int numLayers)
340
    {
341
    return QUATS[0];
342
    }
343

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

    
346
  private int getNumCubitVariants(int numLayers)
347
    {
348
    return 2;
349
    }
350

    
351
///////////////////////////////////////////////////////////////////////////////////////////////////
352

    
353
  int getCubitVariant(int cubit, int numLayers)
354
    {
355
    return cubit<getNumOctahedrons(numLayers) ? 0:1;
356
    }
357

    
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359

    
360
  MeshBase createCubitMesh(int cubit, int numLayers)
361
    {
362
    int variant = getCubitVariant(cubit,numLayers);
363

    
364
    if( mMeshes==null )
365
      {
366
      FactoryCubit factory = FactoryCubit.getInstance();
367
      factory.clear();
368
      mMeshes = new MeshBase[getNumCubitVariants(numLayers)];
369
      }
370

    
371
    if( mMeshes[variant]==null )
372
      {
373
      ObjectShape shape = getObjectShape(cubit,numLayers);
374
      FactoryCubit factory = FactoryCubit.getInstance();
375
      factory.createNewFaceTransform(shape);
376
      mMeshes[variant] = factory.createRoundedSolid(shape);
377
      }
378

    
379
    MeshBase mesh = mMeshes[variant].copy(true);
380
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( getQuat(cubit,numLayers), new Static3D(0,0,0) );
381
    mesh.apply(quat,0xffffffff,0);
382

    
383
    return mesh;
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387

    
388
  int getColor(int face)
389
    {
390
    return FACE_COLORS[face];
391
    }
392

    
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394

    
395
  ObjectSticker retSticker(int face)
396
    {
397
    return mStickers[face/NUM_FACES];
398
    }
399

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401
// SQ6/3 = height of the tetrahedron
402

    
403
  float returnMultiplier()
404
    {
405
    return getNumLayers()/(SQ6/3);
406
    }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409
// PUBLIC API
410

    
411
  public Static3D[] getRotationAxis()
412
    {
413
    return ROT_AXIS;
414
    }
415

    
416
///////////////////////////////////////////////////////////////////////////////////////////////////
417

    
418
  public int[] getBasicAngle()
419
    {
420
    return BASIC_ANGLE;
421
    }
422

    
423
///////////////////////////////////////////////////////////////////////////////////////////////////
424

    
425
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
426
    {
427
    int numLayers = getNumLayers();
428

    
429
    if( mRowChances==null ) mRowChances = getRowChances(numLayers);
430

    
431
    if( curr==0 )
432
      {
433
      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
434
      }
435
    else
436
      {
437
      int newVector = rnd.nextInt(NUM_AXIS-1);
438
      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
439

    
440
      // Correct the situation when we first rotate the largest layer, then a tip (which doesn't
441
      // intersect anything besides the largest layer!) and then we try to rotate again along
442
      // the same axis like 2 rotations before - which carries the risk we rotate the largest
443
      // layer back to its spot again and the three moves end up being only a single tip rotation.
444
      if( curr>=2 && scramble[curr-1][1]==(numLayers-1) && scramble[curr][0]==scramble[curr-2][0] )
445
        {
446
        for(int ax=0; ax<NUM_AXIS; ax++)
447
          {
448
          if( scramble[curr-1][0]!=ax && scramble[curr-2][0]!=ax )
449
            {
450
            scramble[curr][0]=ax;
451
            break;
452
            }
453
          }
454
        }
455
      }
456

    
457
    float rowFloat = rnd.nextFloat();
458

    
459
    for(int row=0; row<numLayers; row++)
460
      {
461
      if( rowFloat<=mRowChances[row] )
462
        {
463
        scramble[curr][1] = row;
464
        break;
465
        }
466
      }
467

    
468
    switch( rnd.nextInt(2) )
469
      {
470
      case 0: scramble[curr][2] = -1; break;
471
      case 1: scramble[curr][2] =  1; break;
472
      }
473
    }
474

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

    
477
  public boolean isSolved()
478
    {
479
    int index = CUBITS[0].mQuatIndex;
480

    
481
    for(int i=1; i<NUM_CUBITS; i++)
482
      {
483
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
484
      }
485

    
486
    return true;
487
    }
488

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

    
491
  public int getObjectName(int numLayers)
492
    {
493
    switch(numLayers)
494
      {
495
      case 3: return R.string.pyra3;
496
      case 4: return R.string.pyra4;
497
      case 5: return R.string.pyra5;
498
      }
499
    return R.string.pyra3;
500
    }
501

    
502
///////////////////////////////////////////////////////////////////////////////////////////////////
503

    
504
  public int getInventor(int numLayers)
505
    {
506
    switch(numLayers)
507
      {
508
      case 3: return R.string.pyra3_inventor;
509
      case 4: return R.string.pyra4_inventor;
510
      case 5: return R.string.pyra5_inventor;
511
      }
512
    return R.string.pyra3_inventor;
513
    }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

    
517
  public int getComplexity(int numLayers)
518
    {
519
    switch(numLayers)
520
      {
521
      case 3: return 4;
522
      case 4: return 6;
523
      case 5: return 8;
524
      }
525
    return 4;
526
    }
527
}
(34-34/41)