Project

General

Profile

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

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

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 oldRotAxis, int oldRow,
339
                                   int numScramble, int remScrambles, int remDoubleScrambles)
340
    {
341
    if( numScramble==1 )
342
      {
343
      scramble[0] = rnd.nextInt(ROTATION_AXIS.length);
344
      }
345
    else
346
      {
347
      int newVector = rnd.nextInt(ROTATION_AXIS.length-2);
348

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

    
362
    float rowFloat = rnd.nextFloat();
363

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

    
373
    int random = rnd.nextInt(remScrambles);
374
    int result = random<remDoubleScrambles ? 2:1;
375
    int sign   = rnd.nextInt(2);
376

    
377
    scramble[2] = sign==0 ? result : -result;
378
    }
379

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

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

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

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

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

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

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

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

    
418
    return -1;
419
    }
420

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

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

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

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

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

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

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

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

    
476
      return true;
477
      }
478

    
479
    return false;
480
    }
481

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

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

    
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491

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

    
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498

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

    
504
///////////////////////////////////////////////////////////////////////////////////////////////////
505

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