Project

General

Profile

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

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

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
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
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 Static3D[] CENTERS = new Static3D[]
101
         {
102
           new Static3D(   DIST_CORNER,   DIST_CORNER,   DIST_CORNER ),
103
           new Static3D(   DIST_CORNER,   DIST_CORNER,  -DIST_CORNER ),
104
           new Static3D(   DIST_CORNER,  -DIST_CORNER,   DIST_CORNER ),
105
           new Static3D(   DIST_CORNER,  -DIST_CORNER,  -DIST_CORNER ),
106
           new Static3D(  -DIST_CORNER,   DIST_CORNER,   DIST_CORNER ),
107
           new Static3D(  -DIST_CORNER,   DIST_CORNER,  -DIST_CORNER ),
108
           new Static3D(  -DIST_CORNER,  -DIST_CORNER,   DIST_CORNER ),
109
           new Static3D(  -DIST_CORNER,  -DIST_CORNER,  -DIST_CORNER ),
110

    
111
           new Static3D(   DIST_CENTER,     XY_CENTER,     XY_CENTER ),
112
           new Static3D(   DIST_CENTER,     XY_CENTER,    -XY_CENTER ),
113
           new Static3D(   DIST_CENTER,    -XY_CENTER,     XY_CENTER ),
114
           new Static3D(   DIST_CENTER,    -XY_CENTER,    -XY_CENTER ),
115

    
116
           new Static3D(  -DIST_CENTER,     XY_CENTER,     XY_CENTER ),
117
           new Static3D(  -DIST_CENTER,     XY_CENTER,    -XY_CENTER ),
118
           new Static3D(  -DIST_CENTER,    -XY_CENTER,     XY_CENTER ),
119
           new Static3D(  -DIST_CENTER,    -XY_CENTER,    -XY_CENTER ),
120

    
121
           new Static3D(   XY_CENTER  ,   DIST_CENTER,     XY_CENTER ),
122
           new Static3D(   XY_CENTER  ,   DIST_CENTER,    -XY_CENTER ),
123
           new Static3D(  -XY_CENTER  ,   DIST_CENTER,     XY_CENTER ),
124
           new Static3D(  -XY_CENTER  ,   DIST_CENTER,    -XY_CENTER ),
125

    
126
           new Static3D(   XY_CENTER  ,  -DIST_CENTER,     XY_CENTER ),
127
           new Static3D(   XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER ),
128
           new Static3D(  -XY_CENTER  ,  -DIST_CENTER,     XY_CENTER ),
129
           new Static3D(  -XY_CENTER  ,  -DIST_CENTER,    -XY_CENTER ),
130

    
131
           new Static3D(   XY_CENTER  ,     XY_CENTER,   DIST_CENTER ),
132
           new Static3D(   XY_CENTER  ,    -XY_CENTER,   DIST_CENTER ),
133
           new Static3D(  -XY_CENTER  ,     XY_CENTER,   DIST_CENTER ),
134
           new Static3D(  -XY_CENTER  ,    -XY_CENTER,   DIST_CENTER ),
135

    
136
           new Static3D(   XY_CENTER  ,     XY_CENTER,  -DIST_CENTER ),
137
           new Static3D(   XY_CENTER  ,    -XY_CENTER,  -DIST_CENTER ),
138
           new Static3D(  -XY_CENTER  ,     XY_CENTER,  -DIST_CENTER ),
139
           new Static3D(  -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, 60, 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
  Static3D[] 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 int randomizeNewRotAxis(Random rnd, int oldRotAxis)
341
    {
342
    int numAxis = ROTATION_AXIS.length;
343

    
344
    if( oldRotAxis == START_AXIS )
345
      {
346
      return rnd.nextInt(numAxis);
347
      }
348
    else
349
      {
350
      int newVector = rnd.nextInt(numAxis-2);
351

    
352
      switch(oldRotAxis)
353
        {
354
        case  0:
355
        case  1: return newVector+2;
356
        case  2:
357
        case  3: return (newVector==0 || newVector==1) ? newVector:newVector+2;
358
        default: return newVector;
359
        }
360
      }
361
    }
362

    
363
///////////////////////////////////////////////////////////////////////////////////////////////////
364

    
365
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
366
    {
367
    float rowFloat = rnd.nextFloat();
368

    
369
    for(int row=0; row<mRowChances.length; row++)
370
      {
371
      if( rowFloat<=mRowChances[row] ) return row;
372
      }
373

    
374
    return 0;
375
    }
376

    
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378
// remember about the double cover or unit quaternions!
379

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

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

    
389
    final float MAX_ERROR = 0.1f;
390
    float dX,dY,dZ,dW;
391

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

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

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

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

    
415
    return -1;
416
    }
417

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

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

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

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

    
453
      q1 = mulQuat(q, 2);
454
      q2 = mulQuat(q,12);
455
      q3 = mulQuat(q,13);
456

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

    
463
      q1 = mulQuat(q, 3);
464
      q2 = mulQuat(q,14);
465
      q3 = mulQuat(q,15);
466

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

    
473
      return true;
474
      }
475

    
476
    return false;
477
    }
478

    
479
///////////////////////////////////////////////////////////////////////////////////////////////////
480
// only needed for solvers - there are no Helicopter solvers ATM)
481

    
482
  public String retObjectString()
483
    {
484
    return "";
485
    }
486

    
487
///////////////////////////////////////////////////////////////////////////////////////////////////
488

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

    
494
///////////////////////////////////////////////////////////////////////////////////////////////////
495

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

    
501
///////////////////////////////////////////////////////////////////////////////////////////////////
502

    
503
  public int getComplexity(int numLayers)
504
    {
505
    return 8;
506
    }
507
}
(21-21/30)