Project

General

Profile

Download (43.8 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObject.java @ d887aa16

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.objectlib.main;
21

    
22
import android.content.SharedPreferences;
23
import android.graphics.Bitmap;
24
import android.graphics.Canvas;
25
import android.graphics.Paint;
26

    
27
import org.distorted.library.effect.Effect;
28
import org.distorted.library.effect.MatrixEffectMove;
29
import org.distorted.library.effect.MatrixEffectQuaternion;
30
import org.distorted.library.effect.MatrixEffectScale;
31
import org.distorted.library.effect.VertexEffectQuaternion;
32
import org.distorted.library.effect.VertexEffectRotate;
33
import org.distorted.library.main.DistortedEffects;
34
import org.distorted.library.main.DistortedLibrary;
35
import org.distorted.library.main.DistortedNode;
36
import org.distorted.library.main.DistortedTexture;
37
import org.distorted.library.main.QuatHelper;
38
import org.distorted.library.mesh.MeshBase;
39
import org.distorted.library.mesh.MeshFile;
40
import org.distorted.library.mesh.MeshJoined;
41
import org.distorted.library.message.EffectListener;
42
import org.distorted.library.type.Dynamic1D;
43
import org.distorted.library.type.Static1D;
44
import org.distorted.library.type.Static3D;
45
import org.distorted.library.type.Static4D;
46

    
47
import org.distorted.objectlib.helpers.FactoryCubit;
48
import org.distorted.objectlib.helpers.FactorySticker;
49
import org.distorted.objectlib.helpers.ObjectLibInterface;
50
import org.distorted.objectlib.helpers.ObjectShape;
51
import org.distorted.objectlib.helpers.ObjectSticker;
52
import org.distorted.objectlib.helpers.ScrambleState;
53
import org.distorted.objectlib.json.JsonReader;
54

    
55
import java.io.DataInputStream;
56
import java.io.IOException;
57
import java.io.InputStream;
58
import java.util.Random;
59

    
60
import static org.distorted.objectlib.main.Movement.MOVEMENT_TETRAHEDRON;
61
import static org.distorted.objectlib.main.Movement.MOVEMENT_HEXAHEDRON;
62
import static org.distorted.objectlib.main.Movement.MOVEMENT_OCTAHEDRON;
63
import static org.distorted.objectlib.main.Movement.MOVEMENT_DODECAHEDRON;
64
import static org.distorted.objectlib.main.Movement.MOVEMENT_SHAPECHANGE;
65

    
66
///////////////////////////////////////////////////////////////////////////////////////////////////
67

    
68
public abstract class TwistyObject
69
  {
70
  public static final int COLOR_YELLOW = 0xffffff00;
71
  public static final int COLOR_WHITE  = 0xffffffff;
72
  public static final int COLOR_BLUE   = 0xff0000ff;
73
  public static final int COLOR_GREEN  = 0xff00bb00;
74
  public static final int COLOR_RED    = 0xff990000;
75
  public static final int COLOR_ORANGE = 0xffff6200;
76
  public static final int COLOR_GREY   = 0xff727c7b;
77
  public static final int COLOR_VIOLET = 0xff7700bb;
78
  public static final int COLOR_BLACK  = 0xff000000;
79

    
80
  public static final int TEXTURE_HEIGHT = 256;
81
  static final int NUM_STICKERS_IN_ROW = 4;
82

    
83
  public static final float SQ2 = (float)Math.sqrt(2);
84
  public static final float SQ3 = (float)Math.sqrt(3);
85
  public static final float SQ5 = (float)Math.sqrt(5);
86
  public static final float SQ6 = (float)Math.sqrt(6);
87

    
88
  private static final float MAX_SIZE_CHANGE = 1.35f;
89
  private static final float MIN_SIZE_CHANGE = 0.75f;
90

    
91
  private static final Static3D CENTER = new Static3D(0,0,0);
92
  private static final int POST_ROTATION_MILLISEC = 500;
93

    
94
  protected int NUM_FACE_COLORS;
95
  protected int NUM_TEXTURES;
96
  protected Cubit[] CUBITS;
97

    
98
  MeshBase[] mMeshes;
99
  Static4D[] OBJECT_QUATS;
100
  int NUM_CUBITS;
101
  int NUM_AXIS;
102
  int NUM_QUATS;
103

    
104
  private int mNumCubitFaces;
105
  private Static3D[] mAxis;
106
  private float[][] mCuts;
107
  private int[] mNumCuts;
108
  private float[][] mOrigPos;
109
  private Static4D mQuat;
110
  private final int[] mNumLayers;
111
  private final float mSize;
112
  private DistortedEffects mEffects;
113
  private VertexEffectRotate mRotateEffect;
114
  private Dynamic1D mRotationAngle;
115
  private Static3D mRotationAxis;
116
  private Static3D mObjectScale;
117
  private int[] mQuatDebug;
118
  private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
119
  private DistortedTexture mTexture;
120
  private float mInitScreenRatio;
121
  private int mSolvedFunctionIndex;
122
  private boolean mIsBandaged;
123
  private float mObjectScreenRatio;
124
  private int[][] mSolvedQuats;
125
  private int[][] mQuatMult;
126
  private int[] mTmpQuats;
127
  private int mNumTexRows, mNumTexCols;
128
  private int mRotRowBitmap;
129
  private int mCurrentRotAxis;
130
  private MeshBase mMesh;
131
  private TwistyObjectScrambler mScrambler;
132
  private Movement mMovement;
133
  private boolean[][] mLayerRotatable;
134
  private int[][][] mEnabled;
135
  private DistortedNode mNode;
136
  private ObjectLibInterface mInterface;
137

    
138
  //////////////////// SOLVED1 ////////////////////////
139

    
140
  private int[] mFaceMap;
141
  private int[][] mScramble;
142
  private int[] mColors;
143

    
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145

    
146
  TwistyObject(InputStream jsonStream, Static4D quat, Static3D move, InputStream meshStream)
147
    {
148
    JsonReader reader = JsonReader.getInstance();
149
    reader.parseJsonFile(jsonStream);
150
    setReader(reader);
151

    
152
    mNumLayers = reader.getNumLayers();
153
    mSize      = reader.getSize();
154
    initialize(quat,move,meshStream);
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  TwistyObject(int[] numLayers, float size, Static4D quat, Static3D move, InputStream meshStream)
160
    {
161
    mNumLayers = numLayers;
162
    mSize      = size;
163
    initialize(quat,move,meshStream);
164
    }
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167

    
168
  private void initialize(Static4D quat, Static3D move, InputStream stream)
169
    {
170
    mQuat = quat;
171
    mOrigPos = getCubitPositions(mNumLayers);
172
    mAxis = getRotationAxis();
173
    mInitScreenRatio = getScreenRatio();
174
    mNumCubitFaces = getNumCubitFaces();
175
    mSolvedFunctionIndex = getSolvedFunctionIndex();
176

    
177
    mCuts = getCuts(mNumLayers);
178
    mNumCuts = new int[mAxis.length];
179
    for(int i=0; i<mAxis.length; i++)
180
      {
181
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
182
      }
183

    
184
    OBJECT_QUATS = getQuats();
185
    NUM_CUBITS  = mOrigPos.length;
186
    NUM_FACE_COLORS = getNumFaceColors();
187
    NUM_TEXTURES = getNumStickerTypes(mNumLayers)*NUM_FACE_COLORS;
188
    NUM_AXIS = mAxis.length;
189
    NUM_QUATS = OBJECT_QUATS.length;
190

    
191
    int scramblingType = getScrambleType();
192
    ScrambleState[] states = getScrambleStates();
193
    mScrambler = new TwistyObjectScrambler(scramblingType,NUM_AXIS,mNumLayers,states);
194

    
195
    boolean bandaged=false;
196

    
197
    for(int c=0; c<NUM_CUBITS; c++)
198
      {
199
      if( mOrigPos[c].length>3 )
200
        {
201
        bandaged=true;
202
        break;
203
        }
204
      }
205

    
206
    mIsBandaged = bandaged;
207
    mQuatDebug = new int[NUM_CUBITS];
208

    
209
    mRotationAngle= new Dynamic1D();
210
    mRotationAxis = new Static3D(1,0,0);
211
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
212

    
213
    mRotationAngleStatic = new Static1D(0);
214
    mRotationAngleMiddle = new Static1D(0);
215
    mRotationAngleFinal  = new Static1D(0);
216

    
217
    mObjectScale = new Static3D(1,1,1);
218
    setObjectRatioNow(1.0f,720);
219

    
220
    MatrixEffectScale scaleEffect = new MatrixEffectScale(mObjectScale);
221
    MatrixEffectQuaternion quatEffect  = new MatrixEffectQuaternion(mQuat, CENTER);
222
    MatrixEffectMove moveEffect = new MatrixEffectMove(move);
223

    
224
    mNumTexCols = NUM_STICKERS_IN_ROW;
225
    mNumTexRows = (NUM_TEXTURES+1)/NUM_STICKERS_IN_ROW;
226

    
227
    if( mNumTexCols*mNumTexRows < NUM_TEXTURES+1 ) mNumTexRows++;
228

    
229
    CUBITS = new Cubit[NUM_CUBITS];
230
    createMeshAndCubits(stream);
231
    createDataStructuresForSolved(mNumLayers);
232

    
233
    mTexture = new DistortedTexture();
234
    mEffects = new DistortedEffects();
235

    
236
    createTexture();
237

    
238
    for(int q=0; q<NUM_QUATS; q++)
239
      {
240
      VertexEffectQuaternion vq = new VertexEffectQuaternion(OBJECT_QUATS[q],CENTER);
241
      vq.setMeshAssociation(0,q);
242
      mEffects.apply(vq);
243
      }
244

    
245
    mEffects.apply(mRotateEffect);
246
    mEffects.apply(quatEffect);
247
    mEffects.apply(scaleEffect);
248
    mEffects.apply(moveEffect);
249

    
250
    mNode = new DistortedNode(mTexture,mEffects,mMesh);
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254

    
255
  private Static3D getPos(float[] origPos)
256
    {
257
    int len = origPos.length/3;
258
    float sumX = 0.0f;
259
    float sumY = 0.0f;
260
    float sumZ = 0.0f;
261

    
262
    for(int i=0; i<len; i++)
263
      {
264
      sumX += origPos[3*i  ];
265
      sumY += origPos[3*i+1];
266
      sumZ += origPos[3*i+2];
267
      }
268

    
269
    sumX /= len;
270
    sumY /= len;
271
    sumZ /= len;
272

    
273
    return new Static3D(sumX,sumY,sumZ);
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

    
278
  private void createMeshAndCubits(InputStream stream)
279
    {
280
    if( stream!=null )
281
      {
282
      DataInputStream dos = new DataInputStream(stream);
283
      mMesh = new MeshFile(dos);
284

    
285
      try
286
        {
287
        stream.close();
288
        }
289
      catch(IOException e)
290
        {
291
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
292
        }
293

    
294
      for(int i=0; i<NUM_CUBITS; i++)
295
        {
296
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
297
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
298
        }
299

    
300
      if( shouldResetTextureMaps() ) resetAllTextureMaps();
301
      }
302
    else
303
      {
304
      MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
305

    
306
      for(int i=0; i<NUM_CUBITS; i++)
307
        {
308
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
309
        cubitMesh[i] = createCubitMesh(i,mNumLayers);
310
        Static3D pos = getPos(mOrigPos[i]);
311
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
312
        cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
313
        }
314

    
315
      mMesh = new MeshJoined(cubitMesh);
316
      resetAllTextureMaps();
317
      }
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

    
322
  private MeshBase createCubitMesh(int cubit, int[] numLayers)
323
    {
324
    int variant = getCubitVariant(cubit,numLayers);
325

    
326
    if( mMeshes==null )
327
      {
328
      FactoryCubit factory = FactoryCubit.getInstance();
329
      factory.clear();
330
      mMeshes = new MeshBase[getNumCubitVariants(numLayers)];
331
      }
332

    
333
    if( mMeshes[variant]==null )
334
      {
335
      ObjectShape shape = getObjectShape(variant);
336
      FactoryCubit factory = FactoryCubit.getInstance();
337
      factory.createNewFaceTransform(shape);
338
      mMeshes[variant] = factory.createRoundedSolid(shape);
339
      }
340

    
341
    MeshBase mesh = mMeshes[variant].copy(true);
342
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( getQuat(cubit,numLayers), new Static3D(0,0,0) );
343
    mesh.apply(quat,0xffffffff,0);
344

    
345
    return mesh;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
  private void createDataStructuresForSolved(int[] numLayers)
351
    {
352
    mTmpQuats = new int[NUM_QUATS];
353
    mSolvedQuats = new int[NUM_CUBITS][];
354

    
355
    for(int c=0; c<NUM_CUBITS; c++)
356
      {
357
      mSolvedQuats[c] = getSolvedQuats(c,numLayers);
358
      }
359
    }
360

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

    
363
  private int getMultQuat(int index1, int index2)
364
    {
365
    if( mQuatMult==null )
366
      {
367
      mQuatMult = new int[NUM_QUATS][NUM_QUATS];
368

    
369
      for(int i=0; i<NUM_QUATS; i++)
370
        for(int j=0; j<NUM_QUATS; j++) mQuatMult[i][j] = -1;
371
      }
372

    
373
    if( mQuatMult[index1][index2]==-1 )
374
      {
375
      mQuatMult[index1][index2] = mulQuat(index1,index2);
376
      }
377

    
378
    return mQuatMult[index1][index2];
379
    }
380

    
381
///////////////////////////////////////////////////////////////////////////////////////////////////
382
// This is used to build internal data structures for the generic 'isSolved()'
383
//
384
// if this is an internal cubit (all faces black): return -1
385
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
386
// Color index, i.e. the index into the 'FACE_COLORS' table.
387
// else (edge or corner cubit, more than one non-black face): return -2.
388

    
389
  protected int retCubitSolvedStatus(int cubit, int[] numLayers)
390
    {
391
    int numNonBlack=0, nonBlackIndex=-1, varColor, cubColor;
392
    int variant = getCubitVariant(cubit,numLayers);
393

    
394
    for(int face=0; face<mNumCubitFaces; face++)
395
      {
396
      varColor = getVariantFaceColor(variant,face,numLayers);
397
      cubColor = getCubitFaceColor(cubit,face,numLayers);
398

    
399
      if( varColor>=0 && cubColor>=0 )
400
        {
401
        numNonBlack++;
402
        nonBlackIndex = cubColor;
403
        }
404
      }
405

    
406
    if( numNonBlack==0 ) return -1;
407
    if( numNonBlack>=2 ) return -2;
408

    
409
    return nonBlackIndex;
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413

    
414
  protected boolean shouldResetTextureMaps()
415
    {
416
    return false;
417
    }
418

    
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420

    
421
  protected int[] buildSolvedQuats(Static3D faceAx, Static4D[] quats)
422
    {
423
    final float MAXD = 0.0001f;
424
    float x = faceAx.get0();
425
    float y = faceAx.get1();
426
    float z = faceAx.get2();
427
    float a,dx,dy,dz,qx,qy,qz;
428
    Static4D quat;
429

    
430
    int len = quats.length;
431
    int place = 0;
432

    
433
    for(int q=1; q<len; q++)
434
      {
435
      quat = quats[q];
436
      qx = quat.get0();
437
      qy = quat.get1();
438
      qz = quat.get2();
439

    
440
           if( x!=0.0f ) { a = qx/x; }
441
      else if( y!=0.0f ) { a = qy/y; }
442
      else               { a = qz/z; }
443

    
444
      dx = a*x-qx;
445
      dy = a*y-qy;
446
      dz = a*z-qz;
447

    
448
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
449
        {
450
        mTmpQuats[place++] = q;
451
        }
452
      }
453

    
454
    if( place!=0 )
455
      {
456
      int[] ret = new int[place];
457
      System.arraycopy(mTmpQuats,0,ret,0,place);
458
      return ret;
459
      }
460

    
461
    return null;
462
    }
463

    
464
///////////////////////////////////////////////////////////////////////////////////////////////////
465

    
466
  private boolean isSolved0()
467
    {
468
    int len, q1,q = CUBITS[0].mQuatIndex;
469
    int[] solved;
470
    boolean skip;
471

    
472
    for(int c=1; c<NUM_CUBITS; c++)
473
      {
474
      q1 = CUBITS[c].mQuatIndex;
475

    
476
      if( q1==q ) continue;
477

    
478
      skip = false;
479
      solved = mSolvedQuats[c];
480
      len = solved==null ? 0:solved.length;
481

    
482
      for(int i=0; i<len; i++)
483
        {
484
        if( q1==getMultQuat(q,solved[i]) )
485
          {
486
          skip = true;
487
          break;
488
          }
489
        }
490

    
491
      if( !skip ) return false;
492
      }
493

    
494
    return true;
495
    }
496

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

    
499
  private int computeScramble(int quatNum, int centerNum)
500
    {
501
    float MAXDIFF = 0.01f;
502
    float[] center= mOrigPos[centerNum];
503
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
504
    Static4D result = QuatHelper.rotateVectorByQuat(sc,OBJECT_QUATS[quatNum]);
505

    
506
    float x = result.get0();
507
    float y = result.get1();
508
    float z = result.get2();
509

    
510
    for(int c=0; c<NUM_CUBITS; c++)
511
      {
512
      float[] cent = mOrigPos[c];
513

    
514
      float qx = cent[0] - x;
515
      float qy = cent[1] - y;
516
      float qz = cent[2] - z;
517

    
518
      if( qx>-MAXDIFF && qx<MAXDIFF &&
519
          qy>-MAXDIFF && qy<MAXDIFF &&
520
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
521
      }
522

    
523
    return -1;
524
    }
525

    
526
///////////////////////////////////////////////////////////////////////////////////////////////////
527
// Dino4 uses this. It is solved if and only if groups of cubits
528
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
529
// or
530
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
531
// are all the same color.
532

    
533
  private boolean isSolved1()
534
    {
535
    if( mScramble==null )
536
      {
537
      mScramble = new int[NUM_QUATS][NUM_CUBITS];
538
      mColors   = new int[NUM_CUBITS];
539

    
540
      for(int q=0; q<NUM_QUATS; q++)
541
        for(int c=0; c<NUM_CUBITS; c++) mScramble[q][c] = computeScramble(q,c);
542
      }
543

    
544
    if( mFaceMap==null )
545
      {
546
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
547
      }
548

    
549
    for(int c=0; c<NUM_CUBITS; c++)
550
      {
551
      int index = mScramble[CUBITS[c].mQuatIndex][c];
552
      mColors[index] = mFaceMap[c];
553
      }
554

    
555
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
556
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
557
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
558

    
559
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
560
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
561
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
562

    
563
    return false;
564
    }
565

    
566
///////////////////////////////////////////////////////////////////////////////////////////////////
567
// Dino6 uses this. It is solved if and only if:
568
//
569
// All four 'X' cubits (i.e. those whose longest edge goes along the X axis) are rotated
570
// by the same quaternion qX, similarly all four 'Y' cubits by the same qY and all four 'Z'
571
// by the same qZ, and then either:
572
//
573
// a) qX = qY = qZ
574
// b) qY = qX*Q2 and qZ = qX*Q8  (i.e. swap of WHITE and YELLOW faces)
575
// c) qX = qY*Q2 and qZ = qY*Q10 (i.e. swap of BLUE and GREEN faces)
576
// d) qX = qZ*Q8 and qY = qZ*Q10 (i.e. swap of RED and BROWN faces)
577
//
578
// BUT: cases b), c) and d) are really the same - it's all just a mirror image of the original.
579
//
580
// X cubits: 0, 2, 8, 10
581
// Y cubits: 1, 3, 9, 11
582
// Z cubits: 4, 5, 6, 7
583

    
584
  private boolean isSolved2()
585
    {
586
    int qX = CUBITS[0].mQuatIndex;
587
    int qY = CUBITS[1].mQuatIndex;
588
    int qZ = CUBITS[4].mQuatIndex;
589

    
590
    if( CUBITS[2].mQuatIndex != qX || CUBITS[8].mQuatIndex != qX || CUBITS[10].mQuatIndex != qX ||
591
        CUBITS[3].mQuatIndex != qY || CUBITS[9].mQuatIndex != qY || CUBITS[11].mQuatIndex != qY ||
592
        CUBITS[5].mQuatIndex != qZ || CUBITS[6].mQuatIndex != qZ || CUBITS[ 7].mQuatIndex != qZ  )
593
      {
594
      return false;
595
      }
596

    
597
    return ( qX==qY && qX==qZ ) || ( qY==mulQuat(qX,2) && qZ==mulQuat(qX,8) );
598
    }
599

    
600
///////////////////////////////////////////////////////////////////////////////////////////////////
601
// Square-2 is solved iff
602
// a) all of its cubits are rotated with the same quat
603
// b) its two 'middle' cubits are rotated with the same quat, the 6 'front' and 6 'back'
604
// edges and corners with this quat multiplied by QUATS[18] (i.e. those are upside down)
605
// and all the 12 left and right edges and corners also with the same quat multiplied by
606
// QUATS[12] - i.e. also upside down.
607

    
608
  private boolean isSolved3()
609
    {
610
    int index = CUBITS[0].mQuatIndex;
611

    
612
    if( CUBITS[1].mQuatIndex!=index ) return false;
613

    
614
    boolean solved = true;
615

    
616
    for(int i=2; i<NUM_CUBITS; i++)
617
      {
618
      if( CUBITS[i].mQuatIndex!=index )
619
        {
620
        solved = false;
621
        break;
622
        }
623
      }
624

    
625
    if( solved ) return true;
626

    
627
    int indexX = mulQuat(index,12);  // QUATS[12] = 180deg (1,0,0)
628
    int indexZ = mulQuat(index,18);  // QUATS[18] = 180deg (0,0,1)
629

    
630
    for(int i= 2; i<        18; i+=2) if( CUBITS[i].mQuatIndex != indexZ ) return false;
631
    for(int i= 3; i<        18; i+=2) if( CUBITS[i].mQuatIndex != indexX ) return false;
632
    for(int i=18; i<NUM_CUBITS; i+=2) if( CUBITS[i].mQuatIndex != indexX ) return false;
633
    for(int i=19; i<NUM_CUBITS; i+=2) if( CUBITS[i].mQuatIndex != indexZ ) return false;
634

    
635
    return true;
636
    }
637

    
638
///////////////////////////////////////////////////////////////////////////////////////////////////
639

    
640
  int computeRow(float[] pos, int axisIndex)
641
    {
642
    int ret=0;
643
    int len = pos.length / 3;
644
    Static3D axis = mAxis[axisIndex];
645
    float axisX = axis.get0();
646
    float axisY = axis.get1();
647
    float axisZ = axis.get2();
648
    float casted;
649

    
650
    for(int i=0; i<len; i++)
651
      {
652
      casted = pos[3*i]*axisX + pos[3*i+1]*axisY + pos[3*i+2]*axisZ;
653
      ret |= computeSingleRow(axisIndex,casted);
654
      }
655

    
656
    return ret;
657
    }
658

    
659
///////////////////////////////////////////////////////////////////////////////////////////////////
660

    
661
  private int computeSingleRow(int axisIndex,float casted)
662
    {
663
    int num = mNumCuts[axisIndex];
664

    
665
    for(int i=0; i<num; i++)
666
      {
667
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
668
      }
669

    
670
    return (1<<num);
671
    }
672

    
673
///////////////////////////////////////////////////////////////////////////////////////////////////
674

    
675
  private boolean wasRotateApplied()
676
    {
677
    return mEffects.exists(mRotateEffect.getID());
678
    }
679

    
680
///////////////////////////////////////////////////////////////////////////////////////////////////
681

    
682
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
683
    {
684
    return (CUBITS[cubit].getRotRow(axis) & rowBitmap) != 0;
685
    }
686

    
687
///////////////////////////////////////////////////////////////////////////////////////////////////
688
// note the minus in front of the sin() - we rotate counterclockwise
689
// when looking towards the direction where the axis increases in values.
690

    
691
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
692
    {
693
    Static3D axis = mAxis[axisIndex];
694

    
695
    while( angleInDegrees<0 ) angleInDegrees += 360;
696
    angleInDegrees %= 360;
697
    
698
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
699
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
700

    
701
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
702
    }
703

    
704
///////////////////////////////////////////////////////////////////////////////////////////////////
705

    
706
  private synchronized void setupPosition(int[][] moves)
707
    {
708
    if( moves!=null )
709
      {
710
      Static4D quat;
711
      int index, axis, rowBitmap, angle;
712
      int[] basic = getBasicAngle();
713

    
714
      for(int[] move: moves)
715
        {
716
        axis     = move[0];
717
        rowBitmap= move[1];
718
        angle    = move[2]*(360/basic[axis]);
719
        quat     = makeQuaternion(axis,angle);
720

    
721
        for(int j=0; j<NUM_CUBITS; j++)
722
          if( belongsToRotation(j,axis,rowBitmap) )
723
            {
724
            index = CUBITS[j].removeRotationNow(quat);
725
            mMesh.setEffectAssociation(j, CUBITS[j].computeAssociation(),index);
726
            }
727
        }
728
      }
729
    }
730

    
731
///////////////////////////////////////////////////////////////////////////////////////////////////
732

    
733
  public int getScrambleType()
734
    {
735
    return 0;
736
    }
737

    
738
///////////////////////////////////////////////////////////////////////////////////////////////////
739

    
740
  int computeBitmapFromRow(int rowBitmap, int axis)
741
    {
742
    if( mIsBandaged )
743
      {
744
      int bitmap, initBitmap=0;
745

    
746
      while( initBitmap!=rowBitmap )
747
        {
748
        initBitmap = rowBitmap;
749

    
750
        for(int cubit=0; cubit<NUM_CUBITS; cubit++)
751
          {
752
          bitmap = CUBITS[cubit].getRotRow(axis);
753
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
754
          }
755
        }
756
      }
757

    
758
    return rowBitmap;
759
    }
760

    
761
///////////////////////////////////////////////////////////////////////////////////////////////////
762
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
763
// Do so only if minimal Error is appropriately low (shape-shifting puzzles - Square-1)
764

    
765
  void clampPos(float[] pos, int offset)
766
    {
767
    float currError, minError = Float.MAX_VALUE;
768
    int minErrorIndex1 = -1;
769
    int minErrorIndex2 = -1;
770

    
771
    float x = pos[offset  ];
772
    float y = pos[offset+1];
773
    float z = pos[offset+2];
774

    
775
    float xo,yo,zo;
776

    
777
    for(int i=0; i<NUM_CUBITS; i++)
778
      {
779
      int len = mOrigPos[i].length / 3;
780

    
781
      for(int j=0; j<len; j++)
782
        {
783
        xo = mOrigPos[i][3*j  ];
784
        yo = mOrigPos[i][3*j+1];
785
        zo = mOrigPos[i][3*j+2];
786

    
787
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
788

    
789
        if( currError<minError )
790
          {
791
          minError = currError;
792
          minErrorIndex1 = i;
793
          minErrorIndex2 = j;
794
          }
795
        }
796
      }
797

    
798
    if( minError< 0.1f ) // TODO: 0.1 ?
799
      {
800
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
801
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
802
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
803
      }
804
    }
805

    
806
///////////////////////////////////////////////////////////////////////////////////////////////////
807
// remember about the double cover or unit quaternions!
808

    
809
  int mulQuat(int q1, int q2)
810
    {
811
    Static4D result = QuatHelper.quatMultiply(OBJECT_QUATS[q1],OBJECT_QUATS[q2]);
812

    
813
    float rX = result.get0();
814
    float rY = result.get1();
815
    float rZ = result.get2();
816
    float rW = result.get3();
817

    
818
    final float MAX_ERROR = 0.1f;
819
    float dX,dY,dZ,dW;
820

    
821
    for(int i=0; i<NUM_QUATS; i++)
822
      {
823
      dX = OBJECT_QUATS[i].get0() - rX;
824
      dY = OBJECT_QUATS[i].get1() - rY;
825
      dZ = OBJECT_QUATS[i].get2() - rZ;
826
      dW = OBJECT_QUATS[i].get3() - rW;
827

    
828
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
829
          dY<MAX_ERROR && dY>-MAX_ERROR &&
830
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
831
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
832

    
833
      dX = OBJECT_QUATS[i].get0() + rX;
834
      dY = OBJECT_QUATS[i].get1() + rY;
835
      dZ = OBJECT_QUATS[i].get2() + rZ;
836
      dW = OBJECT_QUATS[i].get3() + rW;
837

    
838
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
839
          dY<MAX_ERROR && dY>-MAX_ERROR &&
840
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
841
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
842
      }
843

    
844
    return -1;
845
    }
846

    
847
///////////////////////////////////////////////////////////////////////////////////////////////////
848

    
849
  private float getAngle()
850
    {
851
    int pointNum = mRotationAngle.getNumPoints();
852

    
853
    if( pointNum>=1 )
854
      {
855
      return mRotationAngle.getPoint(pointNum-1).get0();
856
      }
857
    else
858
      {
859
      mInterface.reportProblem("points in RotationAngle: "+pointNum);
860
      return 0;
861
      }
862
    }
863

    
864
///////////////////////////////////////////////////////////////////////////////////////////////////
865

    
866
  private void recordQuatsState(String message)
867
    {
868
    StringBuilder quats = new StringBuilder();
869

    
870
    for(int j=0; j<NUM_CUBITS; j++)
871
      {
872
      quats.append(mQuatDebug[j]);
873
      quats.append(" ");
874
      }
875

    
876
    String name = intGetObjectType(mNumLayers).name();
877
    mInterface.reportProblem("obj: "+name+" quats: "+quats.toString());
878
    }
879

    
880
///////////////////////////////////////////////////////////////////////////////////////////////////
881

    
882
  void setLibInterface(ObjectLibInterface inter)
883
    {
884
    mInterface = inter;
885
    }
886

    
887
///////////////////////////////////////////////////////////////////////////////////////////////////
888

    
889
  void initializeObject(int[][] moves)
890
    {
891
    solve();
892
    setupPosition(moves);
893
    }
894

    
895
///////////////////////////////////////////////////////////////////////////////////////////////////
896

    
897
  synchronized void removeRotationNow()
898
    {
899
    float angle = getAngle();
900
    double nearestAngleInRadians = angle*Math.PI/180;
901
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
902
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
903
    float axisX = mAxis[mCurrentRotAxis].get0();
904
    float axisY = mAxis[mCurrentRotAxis].get1();
905
    float axisZ = mAxis[mCurrentRotAxis].get2();
906
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
907

    
908
    mRotationAngle.removeAll();
909
    mRotationAngleStatic.set0(0);
910

    
911
    for(int i=0; i<NUM_CUBITS; i++)
912
      if( belongsToRotation(i, mCurrentRotAxis,mRotRowBitmap) )
913
        {
914
        int index = CUBITS[i].removeRotationNow(quat);
915
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
916
        }
917
    }
918

    
919
///////////////////////////////////////////////////////////////////////////////////////////////////
920

    
921
  long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
922
    {
923
    if( wasRotateApplied() )
924
      {
925
      float angle = getAngle();
926
      mRotationAngleStatic.set0(angle);
927
      mRotationAngleFinal.set0(nearestAngleInDegrees);
928
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
929

    
930
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
931
      mRotationAngle.resetToBeginning();
932
      mRotationAngle.removeAll();
933
      mRotationAngle.add(mRotationAngleStatic);
934
      mRotationAngle.add(mRotationAngleMiddle);
935
      mRotationAngle.add(mRotationAngleFinal);
936
      mRotateEffect.notifyWhenFinished(listener);
937

    
938
      return mRotateEffect.getID();
939
      }
940

    
941
    return 0;
942
    }
943

    
944
///////////////////////////////////////////////////////////////////////////////////////////////////
945

    
946
  synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
947
    {
948
    if( wasRotateApplied() )
949
      {
950
      mCurrentRotAxis = axis;
951
      mRotRowBitmap= computeBitmapFromRow( rowBitmap,axis );
952

    
953
      mRotationAngleStatic.set0(0.0f);
954
      mRotationAxis.set( mAxis[axis] );
955
      mRotationAngle.setDuration(durationMillis);
956
      mRotationAngle.resetToBeginning();
957
      mRotationAngle.add(new Static1D(0));
958
      mRotationAngle.add(new Static1D(angle));
959
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
960
      mRotateEffect.notifyWhenFinished(listener);
961

    
962
      return mRotateEffect.getID();
963
      }
964

    
965
    return 0;
966
    }
967

    
968
///////////////////////////////////////////////////////////////////////////////////////////////////
969

    
970
  void continueRotation(float angleInDegrees)
971
    {
972
    mRotationAngleStatic.set0(angleInDegrees);
973
    }
974

    
975
///////////////////////////////////////////////////////////////////////////////////////////////////
976

    
977
  synchronized void beginNewRotation(int axis, int row )
978
    {
979
    if( axis<0 || axis>=NUM_AXIS )
980
      {
981
      android.util.Log.e("object", "invalid rotation axis: "+axis);
982
      return;
983
      }
984
    if( row<0 || row>=mNumLayers[axis] )
985
      {
986
      android.util.Log.e("object", "invalid rotation row: "+row);
987
      return;
988
      }
989

    
990
    mCurrentRotAxis = axis;
991
    mRotRowBitmap= computeBitmapFromRow( (1<<row),axis );
992
    mRotationAngleStatic.set0(0.0f);
993
    mRotationAxis.set( mAxis[axis] );
994
    mRotationAngle.add(mRotationAngleStatic);
995
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
996
    }
997

    
998
///////////////////////////////////////////////////////////////////////////////////////////////////
999

    
1000
  void setTextureMap(int cubit, int face, int newColor)
1001
    {
1002
    final float ratioW = 1.0f/mNumTexCols;
1003
    final float ratioH = 1.0f/mNumTexRows;
1004
    final Static4D[] maps = new Static4D[mNumCubitFaces];
1005
    int row = (mNumTexRows-1) - newColor/mNumTexCols;
1006
    int col = newColor%mNumTexCols;
1007

    
1008
    maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1009
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1010
    }
1011

    
1012
///////////////////////////////////////////////////////////////////////////////////////////////////
1013

    
1014
  void resetAllTextureMaps()
1015
    {
1016
    final float ratioW = 1.0f/mNumTexCols;
1017
    final float ratioH = 1.0f/mNumTexRows;
1018
    int cubColor, varColor, color, variant, row, col;
1019

    
1020
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
1021
      {
1022
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1023
      variant = getCubitVariant(cubit,mNumLayers);
1024

    
1025
      for(int face=0; face<mNumCubitFaces; face++)
1026
        {
1027
        cubColor = getCubitFaceColor(cubit,face,mNumLayers);
1028
        varColor = getVariantFaceColor(variant,face,mNumLayers);
1029
        color    = cubColor<0 || varColor<0 ? NUM_TEXTURES : varColor*NUM_FACE_COLORS + cubColor;
1030
        row      = (mNumTexRows-1) - color/mNumTexCols;
1031
        col      = color%mNumTexCols;
1032

    
1033
        maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1034
        }
1035

    
1036
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1037
      }
1038
    }
1039

    
1040
///////////////////////////////////////////////////////////////////////////////////////////////////
1041

    
1042
  void releaseResources()
1043
    {
1044
    mTexture.markForDeletion();
1045
    mMesh.markForDeletion();
1046
    mEffects.markForDeletion();
1047

    
1048
    for(int j=0; j<NUM_CUBITS; j++)
1049
      {
1050
      CUBITS[j].releaseResources();
1051
      }
1052
    }
1053

    
1054
///////////////////////////////////////////////////////////////////////////////////////////////////
1055

    
1056
  synchronized void restorePreferences(SharedPreferences preferences)
1057
    {
1058
    boolean error = false;
1059

    
1060
    for(int i=0; i<NUM_CUBITS; i++)
1061
      {
1062
      mQuatDebug[i] = CUBITS[i].restorePreferences(preferences);
1063

    
1064
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<NUM_QUATS)
1065
        {
1066
        CUBITS[i].modifyCurrentPosition(OBJECT_QUATS[mQuatDebug[i]]);
1067
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),mQuatDebug[i]);
1068
        }
1069
      else
1070
        {
1071
        error = true;
1072
        }
1073
      }
1074

    
1075
    if( error )
1076
      {
1077
      for(int i=0; i<NUM_CUBITS; i++)
1078
        {
1079
        CUBITS[i].solve();
1080
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),0);
1081
        }
1082
      recordQuatsState("Failed to restorePreferences");
1083
      }
1084
    }
1085

    
1086
///////////////////////////////////////////////////////////////////////////////////////////////////
1087

    
1088
  void savePreferences(SharedPreferences.Editor editor)
1089
    {
1090
    for(int i=0; i<NUM_CUBITS; i++) CUBITS[i].savePreferences(editor);
1091
    }
1092

    
1093
///////////////////////////////////////////////////////////////////////////////////////////////////
1094
// the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
1095

    
1096
  void createTexture()
1097
    {
1098
    Bitmap bitmap;
1099

    
1100
    Paint paint = new Paint();
1101
    bitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
1102
    Canvas canvas = new Canvas(bitmap);
1103

    
1104
    paint.setAntiAlias(true);
1105
    paint.setTextAlign(Paint.Align.CENTER);
1106
    paint.setStyle(Paint.Style.FILL);
1107

    
1108
    paint.setColor(COLOR_BLACK);
1109
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
1110

    
1111
    int texture = 0;
1112
    FactorySticker factory = FactorySticker.getInstance();
1113

    
1114
    for(int row=0; row<mNumTexRows; row++)
1115
      for(int col=0; col<mNumTexCols; col++)
1116
        {
1117
        if( texture>=NUM_TEXTURES ) break;
1118
        ObjectSticker sticker = retSticker(texture/NUM_FACE_COLORS);
1119
        int color = getColor(texture%NUM_FACE_COLORS);
1120
        factory.drawRoundedPolygon(canvas, paint, col*TEXTURE_HEIGHT, row*TEXTURE_HEIGHT, color, sticker);
1121
        texture++;
1122
        }
1123

    
1124
    if( !mTexture.setTexture(bitmap) )
1125
      {
1126
      int max = DistortedLibrary.getMaxTextureSize();
1127
      mInterface.reportProblem("failed to set texture of size "+bitmap.getWidth()+"x"+bitmap.getHeight()+" max is "+max);
1128
      }
1129
    }
1130

    
1131
///////////////////////////////////////////////////////////////////////////////////////////////////
1132

    
1133
  void setObjectRatioNow(float sc, int nodeMinSize)
1134
    {
1135
    mObjectScreenRatio = sc;
1136
    float scale = mObjectScreenRatio*mInitScreenRatio*nodeMinSize/mSize;
1137
    mObjectScale.set(scale,scale,scale);
1138
    }
1139

    
1140
///////////////////////////////////////////////////////////////////////////////////////////////////
1141

    
1142
  void setObjectRatio(float sizeChange, int nodeMinSize)
1143
    {
1144
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1145

    
1146
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1147
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1148

    
1149
    setObjectRatioNow(mObjectScreenRatio, nodeMinSize);
1150
    }
1151

    
1152
///////////////////////////////////////////////////////////////////////////////////////////////////
1153

    
1154
  float getObjectRatio()
1155
    {
1156
    return mObjectScreenRatio*mInitScreenRatio;
1157
    }
1158

    
1159
///////////////////////////////////////////////////////////////////////////////////////////////////
1160

    
1161
  public float getRatio()
1162
    {
1163
    return mObjectScreenRatio;
1164
    }
1165

    
1166
///////////////////////////////////////////////////////////////////////////////////////////////////
1167

    
1168
  boolean isSolved()
1169
    {
1170
    if( mSolvedFunctionIndex==0 ) return isSolved0();
1171
    if( mSolvedFunctionIndex==1 ) return isSolved1();
1172
    if( mSolvedFunctionIndex==2 ) return isSolved2();
1173
    if( mSolvedFunctionIndex==3 ) return isSolved3();
1174

    
1175
    return false;
1176
    }
1177

    
1178
///////////////////////////////////////////////////////////////////////////////////////////////////
1179
// only called with figuring out which cubit was touched in MODE_REPLACE, which is only used in
1180
// during setting up the initial position in the solver.
1181

    
1182
  int getCubit(float[] point3D)
1183
    {
1184
    float dist, minDist = Float.MAX_VALUE;
1185
    int currentBest=-1;
1186
    float multiplier = mNumLayers[0];
1187

    
1188
    point3D[0] *= multiplier;
1189
    point3D[1] *= multiplier;
1190
    point3D[2] *= multiplier;
1191

    
1192
    for(int i=0; i<NUM_CUBITS; i++)
1193
      {
1194
      dist = CUBITS[i].getDistSquared(point3D);
1195
      if( dist<minDist )
1196
        {
1197
        minDist = dist;
1198
        currentBest = i;
1199
        }
1200
      }
1201

    
1202
    return currentBest;
1203
    }
1204

    
1205
///////////////////////////////////////////////////////////////////////////////////////////////////
1206

    
1207
  int computeNearestAngle(int axis, float angle, float speed)
1208
    {
1209
    int[] basicArray = getBasicAngle();
1210
    int basicAngle   = basicArray[axis>=basicArray.length ? 0 : axis];
1211
    int nearestAngle = 360/basicAngle;
1212

    
1213
    int tmp = (int)((angle+nearestAngle/2)/nearestAngle);
1214
    if( angle< -(nearestAngle*0.5) ) tmp-=1;
1215

    
1216
    if( tmp!=0 ) return nearestAngle*tmp;
1217

    
1218
    return speed> 1.2f ? nearestAngle*(angle>0 ? 1:-1) : 0;
1219
    }
1220

    
1221
///////////////////////////////////////////////////////////////////////////////////////////////////
1222
// INTERNAL API - those are called from 'effects' package
1223
///////////////////////////////////////////////////////////////////////////////////////////////////
1224

    
1225
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
1226
    {
1227
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total);
1228
    }
1229

    
1230
///////////////////////////////////////////////////////////////////////////////////////////////////
1231

    
1232
  public Static4D getRotationQuat()
1233
    {
1234
    return mQuat;
1235
    }
1236

    
1237
///////////////////////////////////////////////////////////////////////////////////////////////////
1238

    
1239
  public float getSize()
1240
    {
1241
    return mSize;
1242
    }
1243

    
1244
///////////////////////////////////////////////////////////////////////////////////////////////////
1245

    
1246
  public void apply(Effect effect, int position)
1247
    {
1248
    mEffects.apply(effect, position);
1249
    }
1250

    
1251
///////////////////////////////////////////////////////////////////////////////////////////////////
1252

    
1253
  public void remove(long effectID)
1254
    {
1255
    mEffects.abortById(effectID);
1256
    }
1257

    
1258
///////////////////////////////////////////////////////////////////////////////////////////////////
1259

    
1260
  public MeshBase getObjectMesh()
1261
    {
1262
    return mMesh;
1263
    }
1264

    
1265
///////////////////////////////////////////////////////////////////////////////////////////////////
1266

    
1267
  public DistortedEffects getObjectEffects()
1268
    {
1269
    return mEffects;
1270
    }
1271

    
1272
///////////////////////////////////////////////////////////////////////////////////////////////////
1273
// PUBLIC API
1274
///////////////////////////////////////////////////////////////////////////////////////////////////
1275

    
1276
  public int getCubitFaceColorIndex(int cubit, int face)
1277
    {
1278
    Static4D texMap = mMesh.getTextureMap(NUM_FACE_COLORS*cubit + face);
1279

    
1280
    int x = (int)(texMap.get0()/texMap.get2());
1281
    int y = (int)(texMap.get1()/texMap.get3());
1282

    
1283
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
1284
    }
1285

    
1286
///////////////////////////////////////////////////////////////////////////////////////////////////
1287

    
1288
  public int[] getNumLayers()
1289
    {
1290
    return mNumLayers;
1291
    }
1292

    
1293
///////////////////////////////////////////////////////////////////////////////////////////////////
1294

    
1295
  public synchronized void solve()
1296
    {
1297
    for(int i=0; i<NUM_CUBITS; i++)
1298
      {
1299
      CUBITS[i].solve();
1300
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
1301
      }
1302
    }
1303

    
1304
///////////////////////////////////////////////////////////////////////////////////////////////////
1305

    
1306
  public DistortedNode getNode()
1307
    {
1308
    return mNode;
1309
    }
1310

    
1311
///////////////////////////////////////////////////////////////////////////////////////////////////
1312

    
1313
  public ObjectType getObjectType()
1314
    {
1315
    return intGetObjectType(mNumLayers);
1316
    }
1317

    
1318
///////////////////////////////////////////////////////////////////////////////////////////////////
1319

    
1320
  public Movement getMovement()
1321
    {
1322
    if( mMovement==null )
1323
      {
1324
      int[] numLayers = getNumLayers();
1325
      if( mCuts==null ) getCuts(numLayers);
1326
      if( mLayerRotatable==null ) mLayerRotatable = getLayerRotatable(numLayers);
1327
      if( mEnabled==null ) mEnabled = getEnabled();
1328

    
1329
      int movementType = getMovementType();
1330
      int movementSplit= getMovementSplit();
1331

    
1332
      switch(movementType)
1333
        {
1334
        case MOVEMENT_TETRAHEDRON : mMovement = new Movement4(mAxis,mCuts,mLayerRotatable,mSize,movementSplit,mEnabled);
1335
                                    break;
1336
        case MOVEMENT_HEXAHEDRON  : mMovement = new Movement6(mAxis,mCuts,mLayerRotatable,mSize,movementSplit,mEnabled);
1337
                                    break;
1338
        case MOVEMENT_OCTAHEDRON  : mMovement = new Movement8(mAxis,mCuts,mLayerRotatable,mSize,movementSplit,mEnabled);
1339
                                    break;
1340
        case MOVEMENT_DODECAHEDRON: mMovement = new Movement12(mAxis,mCuts,mLayerRotatable,mSize,movementSplit,mEnabled);
1341
                                    break;
1342
        case MOVEMENT_SHAPECHANGE : float[] dist3D = getDist3D(numLayers);
1343
                                    mMovement = new MovementC(mAxis,mCuts,mLayerRotatable,mSize,movementSplit,mEnabled,dist3D);
1344
                                    break;
1345
        }
1346
      }
1347
    return mMovement;
1348
    }
1349

    
1350
///////////////////////////////////////////////////////////////////////////////////////////////////
1351

    
1352
  protected void setReader(JsonReader reader)
1353
    {
1354
    // empty
1355
    }
1356

    
1357
///////////////////////////////////////////////////////////////////////////////////////////////////
1358

    
1359
  protected abstract ObjectType intGetObjectType(int[] numLayers);
1360

    
1361
  // for JSON only
1362
  public abstract int getSolvedFunctionIndex();
1363
  public abstract int getMovementType();
1364
  public abstract int getMovementSplit();
1365
  public abstract boolean[][] getLayerRotatable(int[] numLayers);
1366
  public abstract int[][][] getEnabled();
1367
  public abstract float[] getDist3D(int[] numLayers);
1368
  public abstract ScrambleState[] getScrambleStates();
1369
  public abstract float[][] getCuts(int[] numLayers);
1370
  public abstract Static4D[] getQuats();
1371
  public abstract int getNumStickerTypes(int[] numLayers);
1372
  public abstract ObjectSticker retSticker(int sticker);
1373
  public abstract int getCubitVariant(int cubit, int[] numLayers);
1374
  public abstract ObjectShape getObjectShape(int variant);
1375
  public abstract int getNumCubitVariants(int[] numLayers);
1376
  public abstract float[][] getCubitPositions(int[] numLayers);
1377
  public abstract Static4D getQuat(int cubit, int[] numLayers);
1378
  public abstract int[] getSolvedQuats(int cubit, int[] numLayers);
1379
  public abstract int getCubitFaceColor(int cubit, int face, int[] numLayers);
1380
  public abstract int getVariantFaceColor(int variant, int face, int[] numLayers);
1381
  public abstract int getNumFaceColors();
1382
  public abstract int getNumCubitFaces();
1383
  public abstract float getScreenRatio();
1384
  public abstract int getColor(int face);
1385

    
1386
  // not only for JSON
1387
  public abstract Static3D[] getRotationAxis();
1388
  public abstract int[] getBasicAngle();
1389
  public abstract int getNumFaces();
1390
  public abstract String getObjectName();
1391
  public abstract String getInventor();
1392
  public abstract int getYearOfInvention();
1393
  public abstract int getComplexity();
1394
  public abstract int getFOV();
1395
  }
(16-16/18)