Project

General

Profile

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

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

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.RubikSurfaceView;
34

    
35
import java.util.Random;
36

    
37
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
38

    
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40

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

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

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

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

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

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

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

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

    
110
           new Static3D(   DIST_CENTER,     XY_CENTER,     XY_CENTER ),
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

    
115
           new Static3D(  -DIST_CENTER,     XY_CENTER,     XY_CENTER ),
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

    
120
           new Static3D(   XY_CENTER  ,   DIST_CENTER,     XY_CENTER ),
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

    
125
           new Static3D(   XY_CENTER  ,  -DIST_CENTER,     XY_CENTER ),
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

    
130
           new Static3D(   XY_CENTER  ,     XY_CENTER,   DIST_CENTER ),
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

    
135
           new Static3D(   XY_CENTER  ,     XY_CENTER,  -DIST_CENTER ),
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
         };
140

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

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

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

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

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

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

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

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

    
187
  private static MeshBase mCornerMesh, mFaceMesh;
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

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

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198

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

    
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205

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

    
211
///////////////////////////////////////////////////////////////////////////////////////////////////
212

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

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

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

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226

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

    
232
///////////////////////////////////////////////////////////////////////////////////////////////////
233

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

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

    
241
    return cuts;
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

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

    
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252

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

    
258
///////////////////////////////////////////////////////////////////////////////////////////////////
259

    
260
  MeshBase createCubitMesh(int cubit)
261
    {
262
    MeshBase mesh;
263

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

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

    
279
    return mesh;
280
    }
281

    
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283

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

    
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290

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

    
298
    drawRoundedPolygon(canvas, paint, left, 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()
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 int randomizeNewRotAxis(Random rnd, int oldRotAxis)
339
    {
340
    int numAxis = ROTATION_AXIS.length;
341

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

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

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362

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

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

    
372
    return 0;
373
    }
374

    
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376
// remember about the double cover or unit quaternions!
377

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

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

    
387
    final float MAX_ERROR = 0.1f;
388
    float dX,dY,dZ,dW;
389

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

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

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

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

    
413
    return -1;
414
    }
415

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

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

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

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

    
451
      q1 = mulQuat(q, 2);
452
      q2 = mulQuat(q,12);
453
      q3 = mulQuat(q,13);
454

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

    
461
      q1 = mulQuat(q, 3);
462
      q2 = mulQuat(q,14);
463
      q3 = mulQuat(q,15);
464

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

    
471
      return true;
472
      }
473

    
474
    return false;
475
    }
476

    
477
///////////////////////////////////////////////////////////////////////////////////////////////////
478
// only needed for solvers - there are no Helicopter solvers ATM)
479

    
480
  public String retObjectString()
481
    {
482
    return "";
483
    }
484
}
(17-17/21)