Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyPyraminx.java @ 14fe8a07

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.library.main.DistortedEffects;
27
import org.distorted.library.main.DistortedTexture;
28
import org.distorted.library.mesh.MeshBase;
29
import org.distorted.library.mesh.MeshSquare;
30
import org.distorted.library.type.Static3D;
31
import org.distorted.library.type.Static4D;
32
import org.distorted.main.R;
33
import org.distorted.main.RubikSurfaceView;
34

    
35
import java.util.Random;
36

    
37
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
38

    
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40

    
41
public class TwistyPyraminx extends TwistyObject
42
{
43
  static final Static3D[] ROT_AXIS = new Static3D[]
44
         {
45
           new Static3D(     0,-SQ3/3,-SQ6/3),
46
           new Static3D(     0,-SQ3/3,+SQ6/3),
47
           new Static3D(+SQ6/3,+SQ3/3,     0),
48
           new Static3D(-SQ6/3,+SQ3/3,     0),
49
         };
50

    
51
  private static final int[] FACE_COLORS = new int[]
52
         {
53
           COLOR_GREEN , COLOR_YELLOW,
54
           COLOR_BLUE  , COLOR_RED
55
         };
56

    
57
  // computed with res/raw/compute_quats.c
58
  private static final Static4D[] QUATS = new Static4D[]
59
         {
60
           new Static4D(  0.0f,   0.0f,   0.0f,  1.0f),
61
           new Static4D(  0.0f,   1.0f,   0.0f,  0.0f),
62
           new Static4D( SQ2/2,   0.5f,   0.0f,  0.5f),
63
           new Static4D(-SQ2/2,   0.5f,   0.0f,  0.5f),
64
           new Static4D(  0.0f,  -0.5f, -SQ2/2,  0.5f),
65
           new Static4D(  0.0f,  -0.5f,  SQ2/2,  0.5f),
66
           new Static4D( SQ2/2,   0.5f,   0.0f, -0.5f),
67
           new Static4D(-SQ2/2,   0.5f,   0.0f, -0.5f),
68
           new Static4D(  0.0f,  -0.5f, -SQ2/2, -0.5f),
69
           new Static4D(  0.0f,  -0.5f,  SQ2/2, -0.5f),
70
           new Static4D( SQ2/2,   0.0f,  SQ2/2,  0.0f),
71
           new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
72
         };
73

    
74
  private static MeshBase mOctaMesh, mTetraMesh;
75

    
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77

    
78
  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
79
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
80
    {
81
    super(size, size, 30, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
82
    }
83

    
84
///////////////////////////////////////////////////////////////////////////////////////////////////
85

    
86
  private void addTetrahedralLattice(int size, int index, Static3D[] pos)
87
    {
88
    final float DX = 1.0f;
89
    final float DY = SQ2/2;
90
    final float DZ = 1.0f;
91

    
92
    float startX = 0.0f;
93
    float startY =-DY*(size-1)/2;
94
    float startZ = DZ*(size-1)/2;
95

    
96
    for(int layer=0; layer<size; layer++)
97
      {
98
      float currX = startX;
99
      float currY = startY;
100

    
101
      for(int x=0; x<layer+1; x++)
102
        {
103
        float currZ = startZ;
104

    
105
        for(int z=0; z<size-layer; z++)
106
          {
107
          pos[index] = new Static3D(currX,currY,currZ);
108
          index++;
109
          currZ -= DZ;
110
          }
111

    
112
        currX += DX;
113
        }
114

    
115
      startX-=DX/2;
116
      startY+=DY;
117
      startZ-=DZ/2;
118
      }
119
    }
120

    
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122
// there are (n^3-n)/6 octahedrons and ((n+1)^3 - (n+1))/6 tetrahedrons
123

    
124
  Static3D[] getCubitPositions(int size)
125
    {
126
    int numOcta = (size-1)*size*(size+1)/6;
127
    int numTetra= size*(size+1)*(size+2)/6;
128
    Static3D[] ret = new Static3D[numOcta+numTetra];
129

    
130
    addTetrahedralLattice(size-1,      0,ret);
131
    addTetrahedralLattice(size  ,numOcta,ret);
132

    
133
    return ret;
134
    }
135

    
136
///////////////////////////////////////////////////////////////////////////////////////////////////
137

    
138
  Static4D[] getQuats()
139
    {
140
    return QUATS;
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
  int getNumFaces()
146
    {
147
    return FACE_COLORS.length;
148
    }
149

    
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151

    
152
  int getNumStickerTypes()
153
    {
154
    return 1;
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  float[] getCuts(int size)
160
    {
161
    float[] cuts = new float[size-1];
162

    
163
    for(int i=0; i<size-1; i++)
164
      {
165
      cuts[i] = (1.0f-0.25f*size+i)*(SQ6/3);
166
      }
167

    
168
    return cuts;
169
    }
170

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

    
173
  int getNumCubitFaces()
174
    {
175
    return 8;
176
    }
177

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

    
180
  float getScreenRatio()
181
    {
182
    return 0.82f;
183
    }
184

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

    
187
  boolean shouldResetTextureMaps()
188
    {
189
    return false;
190
    }
191

    
192
///////////////////////////////////////////////////////////////////////////////////////////////////
193

    
194
  private int faceColor(int cubit, int axis)
195
    {
196
    float row = CUBITS[cubit].mRotationRow[axis];
197
    return row*row < 0.1f ? axis : NUM_FACES;
198
    }
199

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

    
202
  int getFaceColor(int cubit, int cubitface, int size)
203
    {
204
    if( cubit< (size-1)*size*(size+1)/6 )
205
      {
206
      switch( cubitface )
207
        {
208
        case 0: return faceColor(cubit,0);
209
        case 2: return faceColor(cubit,1);
210
        case 5: return faceColor(cubit,3);
211
        case 7: return faceColor(cubit,2);
212
        default:return NUM_FACES;
213
        }
214
      }
215
    else
216
      {
217
      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
218
      }
219
    }
220

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

    
223
  MeshBase createCubitMesh(int cubit)
224
    {
225
    int numLayers = getNumLayers();
226

    
227
    if( cubit< (numLayers-1)*numLayers*(numLayers+1)/6 )
228
      {
229
      if( mOctaMesh==null ) mOctaMesh = FactoryCubit.getInstance().createOctaMesh();
230
      return mOctaMesh.copy(true);
231
      }
232
    else
233
      {
234
      if( mTetraMesh==null ) mTetraMesh = FactoryCubit.getInstance().createTetraMesh();
235
      return mTetraMesh.copy(true);
236
      }
237
    }
238

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

    
241
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
242
    {
243
    float E = 0.75f;
244
    float F = 0.50f;
245
    float R = 0.06f;
246
    float S = 0.08f;
247
    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
248

    
249
    FactorySticker factory = FactorySticker.getInstance();
250
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254
// SQ6/3 = height of the tetrahedron
255

    
256
  float returnMultiplier()
257
    {
258
    return getNumLayers()/(SQ6/3);
259
    }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262

    
263
  float[] getRowChances()
264
    {
265
    int numLayers = getNumLayers();
266
    int total = numLayers*(numLayers+1)/2;
267
    float running=0.0f;
268
    float[] chances = new float[numLayers];
269

    
270
    for(int i=0; i<numLayers; i++)
271
      {
272
      running += (numLayers-i);
273
      chances[i] = running / total;
274
      }
275

    
276
    return chances;
277
    }
278

    
279
///////////////////////////////////////////////////////////////////////////////////////////////////
280
// PUBLIC API
281

    
282
  public Static3D[] getRotationAxis()
283
    {
284
    return ROT_AXIS;
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288

    
289
  public int getBasicAngle()
290
    {
291
    return 3;
292
    }
293

    
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295

    
296
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
297
    {
298
    int numAxis = ROTATION_AXIS.length;
299

    
300
    if( oldRotAxis == START_AXIS )
301
      {
302
      return rnd.nextInt(numAxis);
303
      }
304
    else
305
      {
306
      int newVector = rnd.nextInt(numAxis-1);
307
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
308
      }
309
    }
310

    
311
///////////////////////////////////////////////////////////////////////////////////////////////////
312

    
313
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
314
    {
315
    float rowFloat = rnd.nextFloat();
316

    
317
    for(int row=0; row<mRowChances.length; row++)
318
      {
319
      if( rowFloat<=mRowChances[row] ) return row;
320
      }
321

    
322
    return 0;
323
    }
324

    
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

    
327
  public boolean isSolved()
328
    {
329
    int index = CUBITS[0].mQuatIndex;
330

    
331
    for(int i=1; i<NUM_CUBITS; i++)
332
      {
333
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
334
      }
335

    
336
    return true;
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
341
// then if it were rotated by quaternion 'quat'.
342
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
343
// middle squares get interchanged. No visible difference!
344
//
345
// So: this is true iff the cubit
346
// a) is a corner or edge and the quaternions are the same
347
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
348

    
349
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
350
    {
351
    if ( cubit.mQuatIndex == quatIndex ) return true;
352

    
353
    int belongsToHowManyFaces = 0;
354
    int numLayers = getNumLayers()-1;
355
    float row;
356
    final float MAX_ERROR = 0.01f;
357

    
358
    for(int i=0; i<NUM_AXIS; i++)
359
      {
360
      row = cubit.mRotationRow[i];
361
      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
362
          (row-numLayers<MAX_ERROR && row-numLayers>-MAX_ERROR)  ) belongsToHowManyFaces++;
363
      }
364

    
365
    switch(belongsToHowManyFaces)
366
      {
367
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
368
      case 1 :                // cubit that lies inside one of the faces
369
               Static3D orig = cubit.getOrigPosition();
370
               Static4D quat1 = QUATS[quatIndex];
371
               Static4D quat2 = QUATS[cubit.mQuatIndex];
372

    
373
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
374
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
375
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
376

    
377
               float row1, row2;
378
               float x1 = rotated1.get0();
379
               float y1 = rotated1.get1();
380
               float z1 = rotated1.get2();
381
               float x2 = rotated2.get0();
382
               float y2 = rotated2.get1();
383
               float z2 = rotated2.get2();
384

    
385
               for(int i=0; i<NUM_AXIS; i++)
386
                 {
387
                 row1 = computeRow(x1,y1,z1,i);
388
                 row2 = computeRow(x2,y2,z2,i);
389

    
390
                 if( (row1==0 && row2==0) || (row1==numLayers && row2==numLayers) ) return true;
391
                 }
392
               return false;
393

    
394
      default: return false;  // edge or corner
395
      }
396
    }
397

    
398
///////////////////////////////////////////////////////////////////////////////////////////////////
399
// only needed for solvers - there are no Pyraminx solvers ATM)
400

    
401
  public String retObjectString()
402
    {
403
    return "";
404
    }
405

    
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407

    
408
  public int getObjectName(int numLayers)
409
    {
410
    switch(numLayers)
411
      {
412
      case 3: return R.string.pyra3;
413
      case 4: return R.string.pyra4;
414
      case 5: return R.string.pyra5;
415
      }
416
    return R.string.pyra3;
417
    }
418

    
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420

    
421
  public int getInventor(int numLayers)
422
    {
423
    switch(numLayers)
424
      {
425
      case 3: return R.string.pyra3_inventor;
426
      case 4: return R.string.pyra4_inventor;
427
      case 5: return R.string.pyra5_inventor;
428
      }
429
    return R.string.pyra3_inventor;
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433

    
434
  public int getComplexity(int numLayers)
435
    {
436
    switch(numLayers)
437
      {
438
      case 3: return 4;
439
      case 4: return 6;
440
      case 5: return 8;
441
      }
442
    return 4;
443
    }
444
}
(25-25/28)