Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyHelicopter.java @ 749ef882

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

    
38
import java.util.Random;
39

    
40
///////////////////////////////////////////////////////////////////////////////////////////////////
41

    
42
public class TwistyHelicopter extends TwistyObject
43
{
44
  private static final int FACES_PER_CUBIT =6;
45

    
46
  // the six rotation axis of a Helicopter. Must be normalized.
47
  static final Static3D[] ROT_AXIS = new Static3D[]
48
         {
49
           new Static3D(     0, +SQ2/2, -SQ2/2),
50
           new Static3D(     0, -SQ2/2, -SQ2/2),
51
           new Static3D(+SQ2/2,      0, -SQ2/2),
52
           new Static3D(-SQ2/2,      0, -SQ2/2),
53
           new Static3D(+SQ2/2, -SQ2/2,      0),
54
           new Static3D(-SQ2/2, -SQ2/2,      0)
55
         };
56

    
57
  private static final int[] FACE_COLORS = new int[]
58
         {
59
           COLOR_YELLOW, COLOR_WHITE,
60
           COLOR_BLUE  , COLOR_GREEN,
61
           COLOR_RED   , COLOR_ORANGE
62
         };
63

    
64
  // All legal rotation quats of a HELICOPTER (same as the Cube!)
65
  private static final Static4D[] QUATS = new Static4D[]
66
         {
67
           new Static4D( 0.00f,  0.00f,  0.00f,  1.00f ),
68
           new Static4D( 1.00f,  0.00f,  0.00f,  0.00f ),
69
           new Static4D( 0.00f,  1.00f,  0.00f,  0.00f ),
70
           new Static4D( 0.00f,  0.00f,  1.00f,  0.00f ),
71

    
72
           new Static4D( SQ2/2,  SQ2/2,  0.00f,  0.00f ),
73
           new Static4D( SQ2/2, -SQ2/2,  0.00f,  0.00f ),
74
           new Static4D( SQ2/2,  0.00f,  SQ2/2,  0.00f ),
75
           new Static4D( SQ2/2,  0.00f, -SQ2/2,  0.00f ),
76
           new Static4D( SQ2/2,  0.00f,  0.00f,  SQ2/2 ),
77
           new Static4D( SQ2/2,  0.00f,  0.00f, -SQ2/2 ),
78
           new Static4D( 0.00f,  SQ2/2,  SQ2/2,  0.00f ),
79
           new Static4D( 0.00f,  SQ2/2, -SQ2/2,  0.00f ),
80
           new Static4D( 0.00f,  SQ2/2,  0.00f,  SQ2/2 ),
81
           new Static4D( 0.00f,  SQ2/2,  0.00f, -SQ2/2 ),
82
           new Static4D( 0.00f,  0.00f,  SQ2/2,  SQ2/2 ),
83
           new Static4D( 0.00f,  0.00f,  SQ2/2, -SQ2/2 ),
84

    
85
           new Static4D( 0.50f,  0.50f,  0.50f,  0.50f ),
86
           new Static4D( 0.50f,  0.50f,  0.50f, -0.50f ),
87
           new Static4D( 0.50f,  0.50f, -0.50f,  0.50f ),
88
           new Static4D( 0.50f,  0.50f, -0.50f, -0.50f ),
89
           new Static4D( 0.50f, -0.50f,  0.50f,  0.50f ),
90
           new Static4D( 0.50f, -0.50f,  0.50f, -0.50f ),
91
           new Static4D( 0.50f, -0.50f, -0.50f,  0.50f ),
92
           new Static4D( 0.50f, -0.50f, -0.50f, -0.50f )
93
         };
94

    
95
  private static final float DIST_CORNER = 0.50f;
96
  private static final float DIST_CENTER = 0.50f;
97
  private static final float XY_CENTER   = DIST_CORNER/3;
98

    
99
  // centers of the 8 corners + 6*4 face triangles ( i.e. of the all 32 cubits)
100
  private static final float[][] CENTERS = new float[][]
101
         {
102
             {   DIST_CORNER,   DIST_CORNER,   DIST_CORNER },
103
             {   DIST_CORNER,   DIST_CORNER,  -DIST_CORNER },
104
             {   DIST_CORNER,  -DIST_CORNER,   DIST_CORNER },
105
             {   DIST_CORNER,  -DIST_CORNER,  -DIST_CORNER },
106
             {  -DIST_CORNER,   DIST_CORNER,   DIST_CORNER },
107
             {  -DIST_CORNER,   DIST_CORNER,  -DIST_CORNER },
108
             {  -DIST_CORNER,  -DIST_CORNER,   DIST_CORNER },
109
             {  -DIST_CORNER,  -DIST_CORNER,  -DIST_CORNER },
110

    
111
             {   DIST_CENTER,     XY_CENTER,     XY_CENTER },
112
             {   DIST_CENTER,     XY_CENTER,    -XY_CENTER },
113
             {   DIST_CENTER,    -XY_CENTER,     XY_CENTER },
114
             {   DIST_CENTER,    -XY_CENTER,    -XY_CENTER },
115

    
116
             {  -DIST_CENTER,     XY_CENTER,     XY_CENTER },
117
             {  -DIST_CENTER,     XY_CENTER,    -XY_CENTER },
118
             {  -DIST_CENTER,    -XY_CENTER,     XY_CENTER },
119
             {  -DIST_CENTER,    -XY_CENTER,    -XY_CENTER },
120

    
121
             {   XY_CENTER  ,   DIST_CENTER,     XY_CENTER },
122
             {   XY_CENTER  ,   DIST_CENTER,    -XY_CENTER },
123
             {  -XY_CENTER  ,   DIST_CENTER,     XY_CENTER },
124
             {  -XY_CENTER  ,   DIST_CENTER,    -XY_CENTER },
125

    
126
             {   XY_CENTER  ,  -DIST_CENTER,     XY_CENTER },
127
             {   XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER },
128
             {  -XY_CENTER  ,  -DIST_CENTER,     XY_CENTER },
129
             {  -XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER },
130

    
131
             {   XY_CENTER  ,     XY_CENTER,   DIST_CENTER },
132
             {   XY_CENTER  ,    -XY_CENTER,   DIST_CENTER },
133
             {  -XY_CENTER  ,     XY_CENTER,   DIST_CENTER },
134
             {  -XY_CENTER  ,    -XY_CENTER,   DIST_CENTER },
135

    
136
             {   XY_CENTER  ,     XY_CENTER,  -DIST_CENTER },
137
             {   XY_CENTER  ,    -XY_CENTER,  -DIST_CENTER },
138
             {  -XY_CENTER  ,     XY_CENTER,  -DIST_CENTER },
139
             {  -XY_CENTER  ,    -XY_CENTER,  -DIST_CENTER },
140
         };
141

    
142
  // Colors of the faces of cubits. Each cubit has 6 faces
143
  private static final int[][] mFaceMap = new int[][]
144
         {
145
           { 4,2,0, 6,6,6 },
146
           { 0,2,5, 6,6,6 },
147
           { 4,0,3, 6,6,6 },
148
           { 5,3,0, 6,6,6 },
149
           { 1,2,4, 6,6,6 },
150
           { 5,2,1, 6,6,6 },
151
           { 4,3,1, 6,6,6 },
152
           { 1,3,5, 6,6,6 },
153

    
154
           { 0 , 6,6,6,6,6 },
155
           { 0 , 6,6,6,6,6 },
156
           { 0 , 6,6,6,6,6 },
157
           { 0 , 6,6,6,6,6 },
158

    
159
           { 1 , 6,6,6,6,6 },
160
           { 1 , 6,6,6,6,6 },
161
           { 1 , 6,6,6,6,6 },
162
           { 1 , 6,6,6,6,6 },
163

    
164
           { 2 , 6,6,6,6,6 },
165
           { 2 , 6,6,6,6,6 },
166
           { 2 , 6,6,6,6,6 },
167
           { 2 , 6,6,6,6,6 },
168

    
169
           { 3 , 6,6,6,6,6 },
170
           { 3 , 6,6,6,6,6 },
171
           { 3 , 6,6,6,6,6 },
172
           { 3 , 6,6,6,6,6 },
173

    
174
           { 4 , 6,6,6,6,6 },
175
           { 4 , 6,6,6,6,6 },
176
           { 4 , 6,6,6,6,6 },
177
           { 4 , 6,6,6,6,6 },
178

    
179
           { 5 , 6,6,6,6,6 },
180
           { 5 , 6,6,6,6,6 },
181
           { 5 , 6,6,6,6,6 },
182
           { 5 , 6,6,6,6,6 },
183
         };
184

    
185
  private static final int[] QUAT_INDICES =
186
      { 0,13,14,1,12,2,3,7,20,6,13,17,7,23,18,12,22,10,8,16,11,21,19,9,3,15,14,0,5,2,1,4 };
187

    
188
  private static MeshBase mCornerMesh, mFaceMesh;
189

    
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191

    
192
  TwistyHelicopter(int size, Static4D quat, DistortedTexture texture,
193
                   MeshSquare mesh, DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
194
    {
195
    super(size, size, quat, texture, mesh, effects, moves, ObjectList.HELI, res, scrWidth);
196
    }
197

    
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199

    
200
  float getScreenRatio()
201
    {
202
    return 1.6f;
203
    }
204

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

    
207
  Static4D[] getQuats()
208
    {
209
    return QUATS;
210
    }
211

    
212
///////////////////////////////////////////////////////////////////////////////////////////////////
213

    
214
  boolean shouldResetTextureMaps()
215
    {
216
    return false;
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  int getNumFaces()
222
    {
223
    return FACE_COLORS.length;
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227

    
228
  int getNumStickerTypes(int numLayers)
229
    {
230
    return 1;
231
    }
232

    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234

    
235
  float[] getCuts(int size)
236
    {
237
    float[] cuts = new float[2];
238

    
239
    cuts[0] = -SQ2/4;
240
    cuts[1] = +SQ2/4;
241

    
242
    return cuts;
243
    }
244

    
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246

    
247
  int getNumCubitFaces()
248
    {
249
    return FACES_PER_CUBIT;
250
    }
251

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

    
254
  float[][] getCubitPositions(int size)
255
    {
256
    return CENTERS;
257
    }
258

    
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260

    
261
  MeshBase createCubitMesh(int cubit, int numLayers)
262
    {
263
    MeshBase mesh;
264

    
265
    if( cubit<8 )
266
      {
267
      if( mCornerMesh==null ) mCornerMesh = FactoryCubit.getInstance().createHelicopterCornerMesh();
268
      mesh = mCornerMesh.copy(true);
269
      }
270
    else
271
      {
272
      if( mFaceMesh==null ) mFaceMesh = FactoryCubit.getInstance().createHelicopterFaceMesh();
273
      mesh = mFaceMesh.copy(true);
274
      }
275

    
276
    int index = QUAT_INDICES[cubit];
277
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( QUATS[index], new Static3D(0,0,0) );
278
    mesh.apply(quat,0xffffffff,0);
279

    
280
    return mesh;
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  int getFaceColor(int cubit, int cubitface, int size)
286
    {
287
    return mFaceMap[cubit][cubitface];
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291

    
292
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top)
293
    {
294
    float R = 0.023f;
295
    float S = 0.035f;
296
    float E = 0.5f;
297
    float[] vertices = { -E+E/4,E/4, E/4,-E+E/4, E/4,E/4};
298

    
299
    FactorySticker factory = FactorySticker.getInstance();
300
    factory.drawRoundedPolygon(canvas, paint, left, top, vertices, S, FACE_COLORS[face], R);
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304

    
305
  float returnMultiplier()
306
    {
307
    return 2.0f;
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

    
312
  float[] getRowChances(int numLayers)
313
    {
314
    float[] chances = new float[3];
315

    
316
    chances[0] = 0.5f;
317
    chances[1] = 0.5f;
318
    chances[2] = 1.0f;
319

    
320
    return chances;
321
    }
322

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324
// PUBLIC API
325

    
326
  public Static3D[] getRotationAxis()
327
    {
328
    return ROT_AXIS;
329
    }
330

    
331
///////////////////////////////////////////////////////////////////////////////////////////////////
332

    
333
  public int getBasicAngle()
334
    {
335
    return 2;
336
    }
337

    
338
///////////////////////////////////////////////////////////////////////////////////////////////////
339

    
340
  public void randomizeNewScramble(int[][] scramble, Random rnd, int num)
341
    {
342
    if( num==0 )
343
      {
344
      scramble[num][0] = rnd.nextInt(ROTATION_AXIS.length);
345
      }
346
    else
347
      {
348
      int newVector = rnd.nextInt(ROTATION_AXIS.length-2);
349

    
350
      switch(scramble[num-1][0])
351
        {
352
        case  0:
353
        case  1: scramble[num][0] = newVector+2;
354
                 break;
355
        case  2:
356
        case  3: scramble[num][0] = (newVector==0 || newVector==1) ? newVector:newVector+2;
357
                 break;
358
        default: scramble[num][0] = newVector;
359
                 break;
360
        }
361
      }
362

    
363
    float rowFloat = rnd.nextFloat();
364

    
365
    for(int row=0; row<mRowChances.length; row++)
366
      {
367
      if( rowFloat<=mRowChances[row] )
368
        {
369
        scramble[num][1] = row;
370
        break;
371
        }
372
      }
373

    
374
    switch( rnd.nextInt(2) )
375
      {
376
      case 0: scramble[num][2] = -1; break;
377
      case 1: scramble[num][2] =  1; break;
378
      }
379
    }
380

    
381
///////////////////////////////////////////////////////////////////////////////////////////////////
382
// remember about the double cover or unit quaternions!
383

    
384
  private int mulQuat(int q1, int q2)
385
    {
386
    Static4D result = RubikSurfaceView.quatMultiply(QUATS[q1],QUATS[q2]);
387

    
388
    float rX = result.get0();
389
    float rY = result.get1();
390
    float rZ = result.get2();
391
    float rW = result.get3();
392

    
393
    final float MAX_ERROR = 0.1f;
394
    float dX,dY,dZ,dW;
395

    
396
    for(int i=0; i<QUATS.length; i++)
397
      {
398
      dX = QUATS[i].get0() - rX;
399
      dY = QUATS[i].get1() - rY;
400
      dZ = QUATS[i].get2() - rZ;
401
      dW = QUATS[i].get3() - rW;
402

    
403
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
404
          dY<MAX_ERROR && dY>-MAX_ERROR &&
405
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
406
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
407

    
408
      dX = QUATS[i].get0() + rX;
409
      dY = QUATS[i].get1() + rY;
410
      dZ = QUATS[i].get2() + rZ;
411
      dW = QUATS[i].get3() + rW;
412

    
413
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
414
          dY<MAX_ERROR && dY>-MAX_ERROR &&
415
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
416
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
417
      }
418

    
419
    return -1;
420
    }
421

    
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423
// The Helicopter is solved if and only if:
424
//
425
// 1) all of its corner cubits are rotated with the same quat
426
// 2) all its face cubits are rotated with the same quat like the corner ones,
427
//    and optionally they also might be turned by a multiple of 90 degrees along
428
//    a vector perpendicular to the face they lie on.
429
//
430
// i.e.
431
// cubits  8, 9,10,11,12,13,14,15 - might be extra QUAT 1,8,9
432
// cubits 16,17,18,19,20,21,22,23 - might be extra QUAT 2,12,13
433
// cubits 24,25,26,27,28,29,30,31 - might be extra QUAT 3,14,15
434

    
435
  public boolean isSolved()
436
    {
437
    int q = CUBITS[0].mQuatIndex;
438

    
439
    if ( CUBITS[1].mQuatIndex == q &&
440
         CUBITS[2].mQuatIndex == q &&
441
         CUBITS[3].mQuatIndex == q &&
442
         CUBITS[4].mQuatIndex == q &&
443
         CUBITS[5].mQuatIndex == q &&
444
         CUBITS[6].mQuatIndex == q &&
445
         CUBITS[7].mQuatIndex == q  )
446
      {
447
      int q1 = mulQuat(q,1);
448
      int q2 = mulQuat(q,8);
449
      int q3 = mulQuat(q,9);
450

    
451
      for(int index=8; index<16; index++)
452
        {
453
        int qIndex = CUBITS[index].mQuatIndex;
454
        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
455
        }
456

    
457
      q1 = mulQuat(q, 2);
458
      q2 = mulQuat(q,12);
459
      q3 = mulQuat(q,13);
460

    
461
      for(int index=16; index<24; index++)
462
        {
463
        int qIndex = CUBITS[index].mQuatIndex;
464
        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
465
        }
466

    
467
      q1 = mulQuat(q, 3);
468
      q2 = mulQuat(q,14);
469
      q3 = mulQuat(q,15);
470

    
471
      for(int index=24; index<32; index++)
472
        {
473
        int qIndex = CUBITS[index].mQuatIndex;
474
        if( qIndex!=q && qIndex!=q1 && qIndex!=q2 && qIndex!=q3 ) return false;
475
        }
476

    
477
      return true;
478
      }
479

    
480
    return false;
481
    }
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484
// only needed for solvers - there are no Helicopter solvers ATM)
485

    
486
  public String retObjectString()
487
    {
488
    return "";
489
    }
490

    
491
///////////////////////////////////////////////////////////////////////////////////////////////////
492

    
493
  public int getObjectName(int numLayers)
494
    {
495
    return R.string.heli3;
496
    }
497

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499

    
500
  public int getInventor(int numLayers)
501
    {
502
    return R.string.heli3_inventor;
503
    }
504

    
505
///////////////////////////////////////////////////////////////////////////////////////////////////
506

    
507
  public int getComplexity(int numLayers)
508
    {
509
    return 8;
510
    }
511
}
(24-24/33)