Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyMegaminx.java @ 6e7146df

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
///////////////////////////////////////////////////////////////////////////////////////////////////
37

    
38
public class TwistyMegaminx extends TwistyMinx
39
{
40
  static final float MEGA_D = 0.05f;
41

    
42
  private static final int NUM_CORNERS = 20;
43
  private static final int NUM_CENTERS = 12;
44
  private static final int NUM_EDGES   = 30;
45

    
46
  private static final int[][] mFaceVertexMap =
47
         {
48
           { 0, 12,  8, 10, 16},
49
           { 0, 12,  4, 14,  2},
50
           { 2, 14,  9, 11, 18},
51
           { 0,  2, 18,  6, 16},
52
           { 8, 13,  5,  4, 12},
53
           { 4,  5, 15,  9, 14},
54
           { 6, 18, 11, 19,  7},
55
           {10, 16,  6,  7, 17},
56
           { 1, 13,  8, 10, 17},
57
           { 1, 13,  5, 15,  3},
58
           { 3, 15,  9, 11, 19},
59
           { 1,  3, 19,  7, 17},
60
         };
61

    
62
  private static MeshBase[] mCenterMeshes, mCornerMeshes, mEdgeMeshes;
63

    
64
  private static final Static4D[] mBasicV, mCurrV;
65

    
66
  static
67
    {
68
    mBasicV = new Static4D[3];
69
    mCurrV  = new Static4D[3];
70

    
71
    mBasicV[0] = new Static4D( (SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
72
    mBasicV[1] = new Static4D(-(SQ5+1)*0.125f, (SQ5-1)*0.125f, -0.250f, 0.0f );
73
    mBasicV[2] = new Static4D(              0,        -0.500f,    0.0f, 0.0f );
74
    };
75

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

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

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

    
86
  private int numCubitsPerCorner(int numLayers)
87
    {
88
    return 3*((numLayers-1)/2)*((numLayers-3)/2) + 1;
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92

    
93
  private int numCubitsPerEdge(int numLayers)
94
    {
95
    return numLayers-2;
96
    }
97

    
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

    
100
  int getNumStickerTypes(int numLayers)
101
    {
102
    return 1; //numLayers;
103
    }
104

    
105
///////////////////////////////////////////////////////////////////////////////////////////////////
106

    
107
  float[] getCuts(int numLayers)
108
    {
109
    float[] cuts = new float[numLayers-1];
110
    float D = (numLayers/3.0f)*MovementMinx.DIST3D;
111
    float E = 2*C1;           // 2*cos(36 deg)
112
    float X = 2*D*E/(1+2*E);  // height of the 'upper' part of a dodecahedron, i.e. put it on a table,
113
                              // its height is then 2*DIST3D, it has one 'lower' part of height X, one
114
                              // 'middle' part of height Y and one upper part of height X again.
115
                              // It's edge length = numLayers/3.0f.
116
    int num = (numLayers-1)/2;
117
    float G = X*(0.5f-MEGA_D)/num; // height of one Layer
118

    
119
    for(int i=0; i<num; i++)
120
      {
121
      cuts[        i] = -MovementMinx.DIST3D + (i+0.5f)*G;
122
      cuts[2*num-1-i] = -cuts[i];
123
      }
124

    
125
    return cuts;
126
    }
127

    
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129

    
130
  private void computeCenter(Static3D[] array, int center)
131
    {
132
    int[] map = mFaceVertexMap[center];
133

    
134
    float x = CORNERS[map[0]].get0() +
135
              CORNERS[map[1]].get0() +
136
              CORNERS[map[2]].get0() +
137
              CORNERS[map[3]].get0() +
138
              CORNERS[map[4]].get0() ;
139

    
140
    float y = CORNERS[map[0]].get1() +
141
              CORNERS[map[1]].get1() +
142
              CORNERS[map[2]].get1() +
143
              CORNERS[map[3]].get1() +
144
              CORNERS[map[4]].get1() ;
145

    
146
    float z = CORNERS[map[0]].get2() +
147
              CORNERS[map[1]].get2() +
148
              CORNERS[map[2]].get2() +
149
              CORNERS[map[3]].get2() +
150
              CORNERS[map[4]].get2() ;
151

    
152
    array[center].set(x/5,y/5,z/5);
153
    }
154

    
155
///////////////////////////////////////////////////////////////////////////////////////////////////
156
// Fill out mCurr{X,Y,Z} by applying appropriate Quat to mBasic{X,Y,Z}
157
// Appropriate one: QUATS[QUAT_INDICES[corner]].
158

    
159
  private void computeBasicVectors(int corner)
160
    {
161
    Static4D quat = QUATS[QUAT_INDICES[corner]];
162

    
163
    mCurrV[0] = RubikSurfaceView.rotateVectorByQuat(mBasicV[0],quat);
164
    mCurrV[1] = RubikSurfaceView.rotateVectorByQuat(mBasicV[1],quat);
165
    mCurrV[2] = RubikSurfaceView.rotateVectorByQuat(mBasicV[2],quat);
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  private void computeCorner(Static3D pos, int numCubitsPerCorner, int numLayers, int corner, int part)
171
    {
172
    float D = numLayers/3.0f;
173
    Static3D corn = CORNERS[corner];
174

    
175
    if( part==0 )
176
      {
177
      pos.set( corn.get0()*D, corn.get1()*D, corn.get2()*D );
178
      }
179
    else
180
      {
181
      float E = 2.0f*(numLayers/6.0f - MEGA_D)/(0.5f*(numLayers-1));
182
      int N = (numCubitsPerCorner-1)/3;
183
      int block = (part-1) % N;
184
      int index = (part-1) / N;
185
      Static4D pri = mCurrV[index];
186
      Static4D sec = mCurrV[(index+2)%3];
187

    
188
      int multP = (block % ((numLayers-3)/2)) + 1;
189
      int multS = (block / ((numLayers-3)/2));
190

    
191
      pos.set( corn.get0()*D + (pri.get0()*multP + sec.get0()*multS)*E,
192
               corn.get1()*D + (pri.get1()*multP + sec.get1()*multS)*E,
193
               corn.get2()*D + (pri.get2()*multP + sec.get2()*multS)*E );
194
      }
195
    }
196

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198
// TODO
199

    
200
  private void computeEdge(Static3D pos, int edge, int part)
201
    {
202

    
203
    }
204

    
205
///////////////////////////////////////////////////////////////////////////////////////////////////
206

    
207
  Static3D[] getCubitPositions(int numLayers)
208
    {
209
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
210
  //  int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
211
    int numCubits = NUM_CORNERS*numCubitsPerCorner ;//+ NUM_EDGES*numCubitsPerEdge + NUM_CENTERS;
212
    int index=0;
213

    
214
    final Static3D[] CENTERS = new Static3D[numCubits];
215

    
216
    for(int corner=0; corner<NUM_CORNERS; corner++)
217
      {
218
      computeBasicVectors(corner);
219

    
220
      for(int part=0; part<numCubitsPerCorner; part++, index++)
221
        {
222
        CENTERS[index] = new Static3D(0,0,0);
223
        computeCorner(CENTERS[index],numCubitsPerCorner,numLayers,corner,part);
224
        }
225
      }
226
/*
227
    for(int edge=0; edge<NUM_EDGES; edge++)
228
      {
229
      for(int part=0; part<numCubitsPerEdge; part++, index++)
230
        {
231
        computeEdge(CENTERS,index, edge, part );
232
        }
233
      }
234

    
235
    for(int center=0; center<NUM_CENTERS; center++, index++)
236
      {
237
      computeCenter(CENTERS,index);
238
      }
239
*/
240
    return CENTERS;
241
    }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

    
245
  private int getQuat(int cubit, int numLayers)
246
    {
247
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
248

    
249
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
250
      {
251
      int corner = cubit/numCubitsPerCorner;
252
      return QUAT_INDICES[corner];
253
      }
254
/*
255
    if( cubit < NUM_CENTERS + NUM_CORNERS*numCubitsPerCorner )
256
      {
257
      switch(cubit-NUM_CORNERS*numCubitsPerCorner)
258
        {
259
        case  0: return  0;
260
        case  1: return 52;
261
        case  2: return  3;
262
        case  3: return 53;
263
        case  4: return 59;
264
        case  5: return 20;
265
        case  6: return 22;
266
        case  7: return 58;
267
        case  8: return 55;
268
        case  9: return 13;
269
        case 10: return  1;
270
        case 11: return 14;
271
        }
272
      }
273

    
274
    int numCubitsPerEdge = numCubitsPerEdge(numLayers);
275

    
276
    // TODO: edges
277
*/
278
    return 0;
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

    
283
  MeshBase createCubitMesh(int cubit, int numLayers)
284
    {
285
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
286
    int numCubitsPerEdge   = numCubitsPerEdge(numLayers);
287
    int index = (numLayers-3)/2;
288
    MeshBase mesh;
289

    
290
    if( mCornerMeshes==null ) mCornerMeshes = new MeshBase[ObjectList.MEGA.getNumVariants()];
291

    
292
//    if( cubit < NUM_CORNERS*numCubitsPerCorner )
293
      {
294
      if( mCornerMeshes[index]==null ) mCornerMeshes[index] = FactoryCubit.getInstance().createMegaminxCornerMesh(numLayers);
295
      mesh = mCornerMeshes[index].copy(true);
296
      }
297
/*
298
    else if( cubit<NUM_CENTERS + NUM_CORNERS*numCubitsPerCorner )
299
      {
300
      if( mCornerMesh==null ) mCornerMesh = FactoryCubit.getInstance().createMegaminxCenterMesh();
301
      mesh = mCornerMesh.copy(true);
302
      }
303
    else
304
      {
305
      if( mEdgeMesh==null ) mEdgeMesh = new MeshBase[(numLayers-1)/2];
306

    
307
      // TODO
308
      }
309
*/
310
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[getQuat(cubit,numLayers)], new Static3D(0,0,0) );
311
    mesh.apply(quat,0xffffffff,0);
312

    
313
    return mesh;
314
    }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317
// TODO
318

    
319
  int getFaceColor(int cubit, int cubitface, int numLayers)
320
    {
321
    if( cubitface<0 || cubitface>2 ) return NUM_TEXTURES*NUM_FACES;
322

    
323
    int numCubitsPerCorner = numCubitsPerCorner(numLayers);
324
    int part  = cubit % numCubitsPerCorner;
325
    int corner= cubit / numCubitsPerCorner;
326

    
327
    if( part==0 )
328
      {
329
      return mCornerFaceMap[corner][cubitface];
330
      }
331
    else
332
      {
333
      int N = (numCubitsPerCorner-1)/3;
334
      int block = (part-1) % N;
335
      int index = (part-1) / N;
336

    
337
      if( block< (numLayers-1)/2 )
338
        {
339
        switch(index)
340
          {
341
          case 0: return cubitface==1 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
342
          case 1: return cubitface==0 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
343
          case 2: return cubitface==2 ? NUM_TEXTURES*NUM_FACES : mCornerFaceMap[corner][cubitface];
344
          }
345
        }
346
      else
347
        {
348
        switch(index)
349
          {
350
          case 0: return cubitface==0 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
351
          case 1: return cubitface==2 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
352
          case 2: return cubitface==1 ? mCornerFaceMap[corner][cubitface] : NUM_TEXTURES*NUM_FACES;
353
          }
354
        }
355
      }
356

    
357
    return NUM_TEXTURES*NUM_FACES;
358
    }
359

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////
361
// TODO
362

    
363
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
364
    {
365
    paint.setColor(FACE_COLORS[face]);
366
    paint.setStyle(Paint.Style.FILL);
367
    canvas.drawRect(left,top,left+TEXTURE_HEIGHT,top+TEXTURE_HEIGHT,paint);
368
    }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371
// PUBLIC API
372

    
373
  public boolean isSolved()
374
    {
375
    int index = CUBITS[0].mQuatIndex;
376

    
377
    for(int i=1; i<NUM_CUBITS; i++)
378
      {
379
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
380
      }
381

    
382
    return true;
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
387
// then if it were rotated by quaternion 'quat'.
388
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
389
// middle squares get interchanged. No visible difference!
390
//
391
// So: this is true iff the cubit
392
// a) is a corner or edge and the quaternions are the same
393
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
394

    
395
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
396
    {
397
    if ( cubit.mQuatIndex == quatIndex ) return true;
398

    
399
    int belongsToHowManyFaces = 0;
400
    int lastLayer = getNumLayers()-1;
401
    float row;
402
    final float MAX_ERROR = 0.01f;
403

    
404
    for(int i=0; i<NUM_AXIS; i++)
405
      {
406
      row = cubit.mRotationRow[i];
407
      if( (row          <MAX_ERROR && row          >-MAX_ERROR) ||
408
          (row-lastLayer<MAX_ERROR && row-lastLayer>-MAX_ERROR)  ) belongsToHowManyFaces++;
409
      }
410

    
411
    switch(belongsToHowManyFaces)
412
      {
413
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
414
      case 1 :                // cubit that lies inside one of the faces
415
               Static3D orig = cubit.getOrigPosition();
416
               Static4D quat1 = QUATS[quatIndex];
417
               Static4D quat2 = QUATS[cubit.mQuatIndex];
418

    
419
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
420
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
421
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
422

    
423
               float row1, row2;
424
               float x1 = rotated1.get0();
425
               float y1 = rotated1.get1();
426
               float z1 = rotated1.get2();
427
               float x2 = rotated2.get0();
428
               float y2 = rotated2.get1();
429
               float z2 = rotated2.get2();
430

    
431
               for(int i=0; i<NUM_AXIS; i++)
432
                 {
433
                 row1 = computeRow(x1,y1,z1,i);
434
                 row2 = computeRow(x2,y2,z2,i);
435

    
436
                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return true;
437
                 }
438
               return false;
439

    
440
      default: return false;  // edge or corner
441
      }
442
    }
443

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445

    
446
  public int getObjectName(int numLayers)
447
    {
448
    if( numLayers==3 ) return R.string.minx3;
449
    if( numLayers==5 ) return R.string.minx4;
450

    
451
    return 0;
452
    }
453

    
454
///////////////////////////////////////////////////////////////////////////////////////////////////
455

    
456
  public int getInventor(int numLayers)
457
    {
458
    if( numLayers==3 ) return R.string.minx3_inventor;
459
    if( numLayers==5 ) return R.string.minx4_inventor;
460

    
461
    return 0;
462
    }
463

    
464
///////////////////////////////////////////////////////////////////////////////////////////////////
465

    
466
  public int getComplexity(int numLayers)
467
    {
468
    if( numLayers==3 ) return 4;
469

    
470
    return 5;
471
    }
472
}
(24-24/30)