Project

General

Profile

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

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

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.RubikSurfaceView;
33

    
34
import java.util.Random;
35

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

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

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

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

    
56
  // computed with res/raw/compute_quats.c
57
  private static final Static4D[] QUATS = new Static4D[]
58
         {
59
           new Static4D(  0.0f,   0.0f,   0.0f,  1.0f),
60
           new Static4D(  0.0f,   1.0f,   0.0f,  0.0f),
61
           new Static4D( SQ2/2,   0.5f,   0.0f,  0.5f),
62
           new Static4D(-SQ2/2,   0.5f,   0.0f,  0.5f),
63
           new Static4D(  0.0f,  -0.5f, -SQ2/2,  0.5f),
64
           new Static4D(  0.0f,  -0.5f,  SQ2/2,  0.5f),
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.0f,  SQ2/2,  0.0f),
70
           new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
71
         };
72

    
73
  private static MeshBase mOctaMesh, mTetraMesh;
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

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

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84

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

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

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

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

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

    
111
        currX += DX;
112
        }
113

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

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

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

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

    
132
    return ret;
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

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

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143

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

    
149
///////////////////////////////////////////////////////////////////////////////////////////////////
150

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

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

    
158
  float getBasicStep()
159
    {
160
    return SQ6/3;
161
    }
162

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

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

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

    
172
  float getScreenRatio()
173
    {
174
    return 0.82f;
175
    }
176

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

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

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

    
186
  private int faceColor(int cubit, int axis)
187
    {
188
    float row = CUBITS[cubit].mRotationRow[axis];
189
    return row*row < 0.1f ? axis : NUM_FACES;
190
    }
191

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

    
194
  int getFaceColor(int cubit, int cubitface, int size)
195
    {
196
    if( cubit< (size-1)*size*(size+1)/6 )
197
      {
198
      switch( cubitface )
199
        {
200
        case 0: return faceColor(cubit,0);
201
        case 2: return faceColor(cubit,1);
202
        case 5: return faceColor(cubit,3);
203
        case 7: return faceColor(cubit,2);
204
        default:return NUM_FACES;
205
        }
206
      }
207
    else
208
      {
209
      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
210
      }
211
    }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

    
215
  MeshBase createCubitMesh(int cubit)
216
    {
217
    int size = getSize();
218

    
219
    if( cubit< (size-1)*size*(size+1)/6 )
220
      {
221
      if( mOctaMesh==null ) mOctaMesh = CubitFactory.getInstance().createOctaMesh();
222
      return mOctaMesh.copy(true);
223
      }
224
    else
225
      {
226
      if( mTetraMesh==null ) mTetraMesh = CubitFactory.getInstance().createTetraMesh();
227
      return mTetraMesh.copy(true);
228
      }
229
    }
230

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

    
233
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left)
234
    {
235
    float E = SQ3/2;
236
    float F =  0.5f;
237
    float R = 0.06f;
238
    float S = 0.08f;
239
    float[] vertices = { -F,-E/3, +F,-E/3, 0.0f,2*E/3};
240

    
241
    drawRoundedPolygon(canvas, paint, left, vertices, S, FACE_COLORS[face], R);
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245
// SQ6/3 = height of the tetrahedron
246

    
247
  float returnMultiplier()
248
    {
249
    return getSize()/(SQ6/3);
250
    }
251

    
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

    
254
  float[] getRowChances()
255
    {
256
    int size = getSize();
257
    int total = size*(size+1)/2;
258
    float running=0.0f;
259
    float[] chances = new float[size];
260

    
261
    for(int i=0; i<size; i++)
262
      {
263
      running += (size-i);
264
      chances[i] = running / total;
265
      }
266

    
267
    return chances;
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271
// PUBLIC API
272

    
273
  public Static3D[] getRotationAxis()
274
    {
275
    return ROT_AXIS;
276
    }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279

    
280
  public int getBasicAngle()
281
    {
282
    return 3;
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

    
287
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
288
    {
289
    int numAxis = ROTATION_AXIS.length;
290

    
291
    if( oldRotAxis == START_AXIS )
292
      {
293
      return rnd.nextInt(numAxis);
294
      }
295
    else
296
      {
297
      int newVector = rnd.nextInt(numAxis-1);
298
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
299
      }
300
    }
301

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303

    
304
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
305
    {
306
    float rowFloat = rnd.nextFloat();
307

    
308
    for(int row=0; row<mRowChances.length; row++)
309
      {
310
      if( rowFloat<=mRowChances[row] ) return row;
311
      }
312

    
313
    return 0;
314
    }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317

    
318
  public boolean isSolved()
319
    {
320
    int index = CUBITS[0].mQuatIndex;
321

    
322
    for(int i=1; i<NUM_CUBITS; i++)
323
      {
324
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
325
      }
326

    
327
    return true;
328
    }
329

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

    
340
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
341
    {
342
    if ( cubit.mQuatIndex == quatIndex ) return true;
343

    
344
    int belongsToHowManyFaces = 0;
345
    int size = getSize()-1;
346
    float row;
347
    final float MAX_ERROR = 0.01f;
348

    
349
    for(int i=0; i<NUM_AXIS; i++)
350
      {
351
      row = cubit.mRotationRow[i];
352
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
353
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
354
      }
355

    
356
    switch(belongsToHowManyFaces)
357
      {
358
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
359
      case 1 :                // cubit that lies inside one of the faces
360
               Static3D orig = cubit.getOrigPosition();
361
               Static4D quat1 = QUATS[quatIndex];
362
               Static4D quat2 = QUATS[cubit.mQuatIndex];
363

    
364
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
365
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
366
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
367

    
368
               float row1, row2;
369
               float x1 = rotated1.get0();
370
               float y1 = rotated1.get1();
371
               float z1 = rotated1.get2();
372
               float x2 = rotated2.get0();
373
               float y2 = rotated2.get1();
374
               float z2 = rotated2.get2();
375

    
376
               for(int i=0; i<NUM_AXIS; i++)
377
                 {
378
                 row1 = computeRow(x1,y1,z1,i);
379
                 row2 = computeRow(x2,y2,z2,i);
380

    
381
                 if( (row1==0 && row2==0) || (row1==size || row2==size) ) return true;
382
                 }
383
               return false;
384

    
385
      default: return false;  // edge or corner
386
      }
387
    }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390
// only needed for solvers - there are no Pyraminx solvers ATM)
391

    
392
  public String retObjectString()
393
    {
394
    return "";
395
    }
396
}
(19-19/21)