Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / TwistyHelicopter.java @ eaf87d1d

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
import java.util.Random;
37

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

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

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

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

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

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

    
83
           new Static4D( 0.50f,  0.50f,  0.50f,  0.50f ),
84
           new Static4D( 0.50f,  0.50f,  0.50f, -0.50f ),
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
         };
92

    
93
  private static final float DIST_CORNER = 0.50f;
94
  private static final float DIST_CENTER = 0.50f;
95
  private static final float XY_CENTER   = DIST_CORNER/3;
96

    
97
  // centers of the 8 corners + 6*4 face triangles ( i.e. of the all 32 cubits)
98
  private static final float[][] CENTERS = new float[][]
99
         {
100
             {   DIST_CORNER,   DIST_CORNER,   DIST_CORNER },
101
             {   DIST_CORNER,   DIST_CORNER,  -DIST_CORNER },
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

    
109
             {   DIST_CENTER,     XY_CENTER,     XY_CENTER },
110
             {   DIST_CENTER,     XY_CENTER,    -XY_CENTER },
111
             {   DIST_CENTER,    -XY_CENTER,     XY_CENTER },
112
             {   DIST_CENTER,    -XY_CENTER,    -XY_CENTER },
113

    
114
             {  -DIST_CENTER,     XY_CENTER,     XY_CENTER },
115
             {  -DIST_CENTER,     XY_CENTER,    -XY_CENTER },
116
             {  -DIST_CENTER,    -XY_CENTER,     XY_CENTER },
117
             {  -DIST_CENTER,    -XY_CENTER,    -XY_CENTER },
118

    
119
             {   XY_CENTER  ,   DIST_CENTER,     XY_CENTER },
120
             {   XY_CENTER  ,   DIST_CENTER,    -XY_CENTER },
121
             {  -XY_CENTER  ,   DIST_CENTER,     XY_CENTER },
122
             {  -XY_CENTER  ,   DIST_CENTER,    -XY_CENTER },
123

    
124
             {   XY_CENTER  ,  -DIST_CENTER,     XY_CENTER },
125
             {   XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER },
126
             {  -XY_CENTER  ,  -DIST_CENTER,     XY_CENTER },
127
             {  -XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER },
128

    
129
             {   XY_CENTER  ,     XY_CENTER,   DIST_CENTER },
130
             {   XY_CENTER  ,    -XY_CENTER,   DIST_CENTER },
131
             {  -XY_CENTER  ,     XY_CENTER,   DIST_CENTER },
132
             {  -XY_CENTER  ,    -XY_CENTER,   DIST_CENTER },
133

    
134
             {   XY_CENTER  ,     XY_CENTER,  -DIST_CENTER },
135
             {   XY_CENTER  ,    -XY_CENTER,  -DIST_CENTER },
136
             {  -XY_CENTER  ,     XY_CENTER,  -DIST_CENTER },
137
             {  -XY_CENTER  ,    -XY_CENTER,  -DIST_CENTER },
138
         };
139

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

    
152
           { 0 , 6,6,6,6,6 },
153
           { 0 , 6,6,6,6,6 },
154
           { 0 , 6,6,6,6,6 },
155
           { 0 , 6,6,6,6,6 },
156

    
157
           { 1 , 6,6,6,6,6 },
158
           { 1 , 6,6,6,6,6 },
159
           { 1 , 6,6,6,6,6 },
160
           { 1 , 6,6,6,6,6 },
161

    
162
           { 2 , 6,6,6,6,6 },
163
           { 2 , 6,6,6,6,6 },
164
           { 2 , 6,6,6,6,6 },
165
           { 2 , 6,6,6,6,6 },
166

    
167
           { 3 , 6,6,6,6,6 },
168
           { 3 , 6,6,6,6,6 },
169
           { 3 , 6,6,6,6,6 },
170
           { 3 , 6,6,6,6,6 },
171

    
172
           { 4 , 6,6,6,6,6 },
173
           { 4 , 6,6,6,6,6 },
174
           { 4 , 6,6,6,6,6 },
175
           { 4 , 6,6,6,6,6 },
176

    
177
           { 5 , 6,6,6,6,6 },
178
           { 5 , 6,6,6,6,6 },
179
           { 5 , 6,6,6,6,6 },
180
           { 5 , 6,6,6,6,6 },
181
         };
182

    
183
  private static final int[] QUAT_INDICES =
184
      { 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 };
185

    
186
  private static MeshBase mCornerMesh, mFaceMesh;
187

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189

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

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  float getScreenRatio()
199
    {
200
    return 1.6f;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
  Static4D[] getQuats()
206
    {
207
    return QUATS;
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  boolean shouldResetTextureMaps()
213
    {
214
    return false;
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

    
219
  int getNumFaces()
220
    {
221
    return FACE_COLORS.length;
222
    }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
  int getNumStickerTypes(int numLayers)
227
    {
228
    return 1;
229
    }
230

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

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

    
237
    cuts[0] = -SQ2/4;
238
    cuts[1] = +SQ2/4;
239

    
240
    return cuts;
241
    }
242

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

    
245
  int getNumCubitFaces()
246
    {
247
    return FACES_PER_CUBIT;
248
    }
249

    
250
///////////////////////////////////////////////////////////////////////////////////////////////////
251

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

    
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258

    
259
  MeshBase createCubitMesh(int cubit, int numLayers)
260
    {
261
    MeshBase mesh;
262

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

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

    
278
    return mesh;
279
    }
280

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

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

    
288
///////////////////////////////////////////////////////////////////////////////////////////////////
289

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

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

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  float returnMultiplier()
304
    {
305
    return 2.0f;
306
    }
307

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309

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

    
314
    chances[0] = 0.5f;
315
    chances[1] = 0.5f;
316
    chances[2] = 1.0f;
317

    
318
    return chances;
319
    }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322
// PUBLIC API
323

    
324
  public Static3D[] getRotationAxis()
325
    {
326
    return ROT_AXIS;
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

    
331
  public int getBasicAngle()
332
    {
333
    return 2;
334
    }
335

    
336
///////////////////////////////////////////////////////////////////////////////////////////////////
337

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

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

    
361
    float rowFloat = rnd.nextFloat();
362

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

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

    
379
///////////////////////////////////////////////////////////////////////////////////////////////////
380
// remember about the double cover or unit quaternions!
381

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

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

    
391
    final float MAX_ERROR = 0.1f;
392
    float dX,dY,dZ,dW;
393

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

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

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

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

    
417
    return -1;
418
    }
419

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

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

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

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

    
455
      q1 = mulQuat(q, 2);
456
      q2 = mulQuat(q,12);
457
      q3 = mulQuat(q,13);
458

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

    
465
      q1 = mulQuat(q, 3);
466
      q2 = mulQuat(q,14);
467
      q3 = mulQuat(q,15);
468

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

    
475
      return true;
476
      }
477

    
478
    return false;
479
    }
480

    
481
///////////////////////////////////////////////////////////////////////////////////////////////////
482
// only needed for solvers - there are no Helicopter solvers ATM)
483

    
484
  public String retObjectString()
485
    {
486
    return "";
487
    }
488

    
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490

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

    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497

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

    
503
///////////////////////////////////////////////////////////////////////////////////////////////////
504

    
505
  public int getComplexity(int numLayers)
506
    {
507
    return 8;
508
    }
509
}
(26-26/35)