Project

General

Profile

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

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

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
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import org.distorted.helpers.FactoryCubit;
27
import org.distorted.helpers.FactorySticker;
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 MeshBase[] mMeshes;
121
  private static float[] mRowChances;
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

    
125
  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
126
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
127
    {
128
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
129
    }
130

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

    
133
  private float[] getRowChances(int numLayers)
134
    {
135
    int total = numLayers*(numLayers+1)/2;
136
    float running=0.0f;
137
    float[] chances = new float[numLayers];
138

    
139
    for(int i=0; i<numLayers; i++)
140
      {
141
      running += (numLayers-i);
142
      chances[i] = running / total;
143
      }
144

    
145
    return chances;
146
    }
147

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

    
150
  private void addTetrahedralLattice(int size, int index, float[][] pos)
151
    {
152
    final float DX = 1.0f;
153
    final float DY = SQ2/2;
154
    final float DZ = 1.0f;
155

    
156
    float startX = 0.0f;
157
    float startY =-DY*(size-1)/2;
158
    float startZ = DZ*(size-1)/2;
159

    
160
    for(int layer=0; layer<size; layer++)
161
      {
162
      float currX = startX;
163
      float currY = startY;
164

    
165
      for(int x=0; x<layer+1; x++)
166
        {
167
        float currZ = startZ;
168

    
169
        for(int z=0; z<size-layer; z++)
170
          {
171
          pos[index] = new float[] {currX,currY,currZ};
172
          index++;
173
          currZ -= DZ;
174
          }
175

    
176
        currX += DX;
177
        }
178

    
179
      startX-=DX/2;
180
      startY+=DY;
181
      startZ-=DZ/2;
182
      }
183
    }
184

    
185
///////////////////////////////////////////////////////////////////////////////////////////////////
186
// there are (n^3-n)/6 octahedrons and ((n+1)^3 - (n+1))/6 tetrahedrons
187

    
188
  float[][] getCubitPositions(int size)
189
    {
190
    int numOcta = (size-1)*size*(size+1)/6;
191
    int numTetra= size*(size+1)*(size+2)/6;
192
    float[][] ret = new float[numOcta+numTetra][];
193

    
194
    addTetrahedralLattice(size-1,      0,ret);
195
    addTetrahedralLattice(size  ,numOcta,ret);
196

    
197
    return ret;
198
    }
199

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201

    
202
  Static4D[] getQuats()
203
    {
204
    return QUATS;
205
    }
206

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

    
209
  int getNumFaces()
210
    {
211
    return FACE_COLORS.length;
212
    }
213

    
214
///////////////////////////////////////////////////////////////////////////////////////////////////
215

    
216
  int getNumStickerTypes(int numLayers)
217
    {
218
    return STICKERS.length;
219
    }
220

    
221
///////////////////////////////////////////////////////////////////////////////////////////////////
222

    
223
  float[][] getCuts(int size)
224
    {
225
    float[][] cuts = new float[4][size-1];
226

    
227
    for(int i=0; i<size-1; i++)
228
      {
229
      float cut = (1.0f-0.25f*size+i)*(SQ6/3);
230
      cuts[0][i] = cut;
231
      cuts[1][i] = cut;
232
      cuts[2][i] = cut;
233
      cuts[3][i] = cut;
234
      }
235

    
236
    return cuts;
237
    }
238

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

    
241
  int getNumCubitFaces()
242
    {
243
    return 8;
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
  float getScreenRatio()
249
    {
250
    return SCREEN_RATIO;
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254

    
255
  boolean shouldResetTextureMaps()
256
    {
257
    return false;
258
    }
259

    
260
///////////////////////////////////////////////////////////////////////////////////////////////////
261

    
262
  private int getNumOctahedrons(int numLayers)
263
    {
264
    return (numLayers-1)*numLayers*(numLayers+1)/6;
265
    }
266

    
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

    
269
  private int faceColor(int cubit, int axis)
270
    {
271
    return CUBITS[cubit].mRotationRow[axis] == 1 ? axis : NUM_FACES;
272
    }
273

    
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275

    
276
  int getFaceColor(int cubit, int cubitface, int size)
277
    {
278
    if( cubit< (size-1)*size*(size+1)/6 )
279
      {
280
      switch( cubitface )
281
        {
282
        case 0: return faceColor(cubit,0);
283
        case 2: return faceColor(cubit,1);
284
        case 5: return faceColor(cubit,3);
285
        case 7: return faceColor(cubit,2);
286
        default:return NUM_FACES;
287
        }
288
      }
289
    else
290
      {
291
      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
292
      }
293
    }
294

    
295
///////////////////////////////////////////////////////////////////////////////////////////////////
296

    
297
  MeshBase createCubitMesh(int cubit, int numLayers)
298
    {
299
    if( mMeshes==null )
300
      {
301
      FactoryCubit factory = FactoryCubit.getInstance();
302
      factory.clear();
303
      mMeshes = new MeshBase[2];
304
      }
305

    
306
    MeshBase mesh;
307
    int numO = getNumOctahedrons(numLayers);
308

    
309
    if( cubit<numO )
310
      {
311
      if( mMeshes[0]==null )
312
        {
313
        float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,6,2,2} };
314
        int[] bandIndexes   = new int[] { 0,0,0,0,0,0,0,0 };
315
        float[][] corners   = new float[][] { {0.04f,0.20f} };
316
        int[] cornerIndexes = new int[] { 0,0,0,0,0,0 };
317
        float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
318
        int[] centerIndexes = new int[] { 0,0,0,0,0,0 };
319

    
320
        FactoryCubit factory = FactoryCubit.getInstance();
321

    
322
        factory.createNewFaceTransform(VERTICES_OCTA,VERT_INDEXES_OCTA);
323
        mMeshes[0] = factory.createRoundedSolid(VERTICES_OCTA, VERT_INDEXES_OCTA,
324
                                                bands, bandIndexes,
325
                                                corners, cornerIndexes,
326
                                                centers, centerIndexes,
327
                                                getNumCubitFaces() );
328
        }
329
      mesh = mMeshes[0].copy(true);
330
      }
331
    else
332
      {
333
      if( mMeshes[1]==null )
334
        {
335
        float[][] bands     = new float[][] { {0.05f,35,0.5f,0.8f,6,2,2} };
336
        int[] bandIndexes   = new int[] { 0,0,0,0 };
337
        float[][] corners   = new float[][] { {0.06f,0.15f} };
338
        int[] cornerIndexes = new int[] { 0,0,0,0 };
339
        float[][] centers   = new float[][] { {0.0f, 0.0f, 0.0f} };
340
        int[] centerIndexes = new int[] { 0,0,0,0 };
341

    
342
        FactoryCubit factory = FactoryCubit.getInstance();
343

    
344
        factory.createNewFaceTransform(VERTICES_TETRA,VERT_INDEXES_TETRA);
345
        mMeshes[1] = factory.createRoundedSolid(VERTICES_TETRA, VERT_INDEXES_TETRA,
346
                                                bands, bandIndexes,
347
                                                corners, cornerIndexes,
348
                                                centers, centerIndexes,
349
                                                getNumCubitFaces() );
350

    
351
        factory.printStickerCoords();
352
        }
353
      mesh = mMeshes[1].copy(true);
354
      }
355

    
356
    return mesh;
357
    }
358

    
359
///////////////////////////////////////////////////////////////////////////////////////////////////
360

    
361
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
362
    {
363
    float R = 0.06f;
364
    float S = 0.08f;
365

    
366
    FactorySticker factory = FactorySticker.getInstance();
367
    factory.drawRoundedPolygon(canvas, paint, left, top, STICKERS[0], S, FACE_COLORS[face], R);
368
    }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371
// SQ6/3 = height of the tetrahedron
372

    
373
  float returnMultiplier()
374
    {
375
    return getNumLayers()/(SQ6/3);
376
    }
377

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
// PUBLIC API
380

    
381
  public Static3D[] getRotationAxis()
382
    {
383
    return ROT_AXIS;
384
    }
385

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

    
388
  public int[] getBasicAngle()
389
    {
390
    return BASIC_ANGLE;
391
    }
392

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

    
395
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
396
    {
397
    int numLayers = getNumLayers();
398

    
399
    if( mRowChances==null ) mRowChances = getRowChances(numLayers);
400

    
401
    if( curr==0 )
402
      {
403
      scramble[curr][0] = rnd.nextInt(NUM_AXIS);
404
      }
405
    else
406
      {
407
      int newVector = rnd.nextInt(NUM_AXIS-1);
408
      scramble[curr][0] = (newVector>=scramble[curr-1][0] ? newVector+1 : newVector);
409

    
410
      // Correct the situation when we first rotate the largest layer, then a tip (which doesn't
411
      // intersect anything besides the largest layer!) and then we try to rotate again along
412
      // the same axis like 2 rotations before - which carries the risk we rotate the largest
413
      // layer back to its spot again and the three moves end up being only a single tip rotation.
414
      if( curr>=2 && scramble[curr-1][1]==(numLayers-1) && scramble[curr][0]==scramble[curr-2][0] )
415
        {
416
        for(int ax=0; ax<NUM_AXIS; ax++)
417
          {
418
          if( scramble[curr-1][0]!=ax && scramble[curr-2][0]!=ax )
419
            {
420
            scramble[curr][0]=ax;
421
            break;
422
            }
423
          }
424
        }
425
      }
426

    
427
    float rowFloat = rnd.nextFloat();
428

    
429
    for(int row=0; row<numLayers; row++)
430
      {
431
      if( rowFloat<=mRowChances[row] )
432
        {
433
        scramble[curr][1] = row;
434
        break;
435
        }
436
      }
437

    
438
    switch( rnd.nextInt(2) )
439
      {
440
      case 0: scramble[curr][2] = -1; break;
441
      case 1: scramble[curr][2] =  1; break;
442
      }
443
    }
444

    
445
///////////////////////////////////////////////////////////////////////////////////////////////////
446

    
447
  public boolean isSolved()
448
    {
449
    int index = CUBITS[0].mQuatIndex;
450

    
451
    for(int i=1; i<NUM_CUBITS; i++)
452
      {
453
      if( thereIsVisibleDifference(CUBITS[i], index) ) return false;
454
      }
455

    
456
    return true;
457
    }
458

    
459
////////////////////////////////////////////////////////////////////////
460
// only needed for solvers - there are no Pyraminx solvers ATM)
461

    
462
  public String retObjectString()
463
    {
464
    return "";
465
    }
466

    
467
///////////////////////////////////////////////////////////////////////////////////////////////////
468

    
469
  public int getObjectName(int numLayers)
470
    {
471
    switch(numLayers)
472
      {
473
      case 3: return R.string.pyra3;
474
      case 4: return R.string.pyra4;
475
      case 5: return R.string.pyra5;
476
      }
477
    return R.string.pyra3;
478
    }
479

    
480
///////////////////////////////////////////////////////////////////////////////////////////////////
481

    
482
  public int getInventor(int numLayers)
483
    {
484
    switch(numLayers)
485
      {
486
      case 3: return R.string.pyra3_inventor;
487
      case 4: return R.string.pyra4_inventor;
488
      case 5: return R.string.pyra5_inventor;
489
      }
490
    return R.string.pyra3_inventor;
491
    }
492

    
493
///////////////////////////////////////////////////////////////////////////////////////////////////
494

    
495
  public int getComplexity(int numLayers)
496
    {
497
    switch(numLayers)
498
      {
499
      case 3: return 4;
500
      case 4: return 6;
501
      case 5: return 8;
502
      }
503
    return 4;
504
    }
505
}
(34-34/41)