Project

General

Profile

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

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

1 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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.content.res.Resources;
24
import android.graphics.Bitmap;
25
import android.graphics.Canvas;
26
import android.graphics.Paint;
27
28
import com.google.firebase.crashlytics.FirebaseCrashlytics;
29
30
import org.distorted.library.effect.Effect;
31
import org.distorted.library.effect.MatrixEffectMove;
32
import org.distorted.library.effect.MatrixEffectQuaternion;
33
import org.distorted.library.effect.MatrixEffectScale;
34
import org.distorted.library.effect.VertexEffectQuaternion;
35
import org.distorted.library.effect.VertexEffectRotate;
36
import org.distorted.library.main.DistortedEffects;
37
import org.distorted.library.main.DistortedLibrary;
38
import org.distorted.library.main.DistortedNode;
39
import org.distorted.library.main.DistortedTexture;
40 a706f8e0 Leszek Koltunski
import org.distorted.library.main.QuatHelper;
41 29b82486 Leszek Koltunski
import org.distorted.library.mesh.MeshBase;
42
import org.distorted.library.mesh.MeshFile;
43
import org.distorted.library.mesh.MeshJoined;
44
import org.distorted.library.mesh.MeshSquare;
45
import org.distorted.library.message.EffectListener;
46
import org.distorted.library.type.Dynamic1D;
47
import org.distorted.library.type.Static1D;
48
import org.distorted.library.type.Static3D;
49
import org.distorted.library.type.Static4D;
50
import org.distorted.objectlib.BuildConfig;
51 198c5bf0 Leszek Koltunski
import org.distorted.objectlib.helpers.FactoryCubit;
52
import org.distorted.objectlib.helpers.FactorySticker;
53
import org.distorted.objectlib.helpers.ObjectShape;
54
import org.distorted.objectlib.helpers.ObjectSticker;
55
import org.distorted.objectlib.helpers.ScrambleState;
56 29b82486 Leszek Koltunski
57
import java.io.DataInputStream;
58
import java.io.IOException;
59
import java.io.InputStream;
60
import java.util.Random;
61
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63
64
public abstract class TwistyObject extends DistortedNode
65
  {
66
  public static final int COLOR_YELLOW = 0xffffff00;
67
  public static final int COLOR_WHITE  = 0xffffffff;
68
  public static final int COLOR_BLUE   = 0xff0000ff;
69
  public static final int COLOR_GREEN  = 0xff00bb00;
70
  public static final int COLOR_RED    = 0xff990000;
71
  public static final int COLOR_ORANGE = 0xffff6200;
72
  public static final int COLOR_GREY   = 0xff727c7b;
73
  public static final int COLOR_VIOLET = 0xff7700bb;
74
  public static final int COLOR_BLACK  = 0xff000000;
75
76
  public static final int TEXTURE_HEIGHT = 256;
77
  static final int NUM_STICKERS_IN_ROW = 4;
78
79
  public static final float SQ2 = (float)Math.sqrt(2);
80
  public static final float SQ3 = (float)Math.sqrt(3);
81
  public static final float SQ5 = (float)Math.sqrt(5);
82
  public static final float SQ6 = (float)Math.sqrt(6);
83
84
  private static final float NODE_RATIO = 1.60f;
85
  private static final float MAX_SIZE_CHANGE = 1.35f;
86
  private static final float MIN_SIZE_CHANGE = 0.75f;
87
88
  private static final Static3D CENTER = new Static3D(0,0,0);
89
  private static final int POST_ROTATION_MILLISEC = 500;
90
91
  protected final int NUM_FACE_COLORS;
92
  protected final int NUM_TEXTURES;
93
  protected final Cubit[] CUBITS;
94
95
  MeshBase[] mMeshes;
96
  final Static4D[] OBJECT_QUATS;
97
  final int NUM_CUBITS;
98
  final int NUM_AXIS;
99
  final int NUM_QUATS;
100
101
  private final int mNumCubitFaces;
102
  private final Static3D[] mAxis;
103
  private final float[][] mCuts;
104
  private final int[] mNumCuts;
105
  private final int mNodeSize;
106
  private final float[][] mOrigPos;
107
  private final Static3D mNodeScale;
108
  private final Static4D mQuat;
109
  private final int mNumLayers, mRealSize;
110
  private final DistortedEffects mEffects;
111
  private final VertexEffectRotate mRotateEffect;
112
  private final Dynamic1D mRotationAngle;
113
  private final Static3D mRotationAxis;
114
  private final Static3D mObjectScale;
115
  private final int[] mQuatDebug;
116
  private final float mCameraDist;
117
  private final Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
118
  private final DistortedTexture mTexture;
119
  private final float mInitScreenRatio;
120
  private final int mSolvedFunctionIndex;
121
  private final boolean mIsBandaged;
122
  private float mObjectScreenRatio;
123
  private int[][] mSolvedQuats;
124
  private int[][] mQuatMult;
125
  private int[] mTmpQuats;
126
  private int mNumTexRows, mNumTexCols;
127
  private int mRotRowBitmap;
128
  private int mRotAxis;
129
  private MeshBase mMesh;
130
  private final TwistyObjectScrambler mScrambler;
131
132
  //////////////////// SOLVED1 ////////////////////////
133
134
  private int[] mFaceMap;
135
  private int[][] mScramble;
136
  private int[] mColors;
137
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139
140 ecf3d6e3 Leszek Koltunski
  TwistyObject(int numLayers, int realSize, Static4D quat, Static3D move, DistortedTexture nodeTexture,
141 81141862 Leszek Koltunski
               MeshSquare nodeMesh, DistortedEffects nodeEffects, Resources res, int screenWidth)
142 29b82486 Leszek Koltunski
    {
143
    super(nodeTexture,nodeEffects,nodeMesh);
144
145
    mNodeSize = screenWidth;
146
147
    resizeFBO(mNodeSize, (int)(NODE_RATIO*mNodeSize));
148
149
    mNumLayers = numLayers;
150
    mRealSize = realSize;
151
    mOrigPos = getCubitPositions(mNumLayers);
152
    mAxis = getRotationAxis();
153
    mInitScreenRatio = getScreenRatio();
154
    mObjectScreenRatio = 1.0f;
155
    mNumCubitFaces = getNumCubitFaces();
156
    mSolvedFunctionIndex = getSolvedFunctionIndex();
157
158
    mCuts = getCuts(mNumLayers);
159
    mNumCuts = new int[mAxis.length];
160
    if( mCuts==null ) for(int i=0; i<mAxis.length; i++) mNumCuts[i] = 0;
161
    else              for(int i=0; i<mAxis.length; i++) mNumCuts[i] = mCuts[i].length;
162
163
    OBJECT_QUATS = getQuats();
164
    NUM_CUBITS  = mOrigPos.length;
165
    NUM_FACE_COLORS = getNumFaceColors();
166
    NUM_TEXTURES = getNumStickerTypes(mNumLayers)*NUM_FACE_COLORS;
167
    NUM_AXIS = mAxis.length;
168
    NUM_QUATS = OBJECT_QUATS.length;
169
170
    int scramblingType = getScrambleType();
171
    ScrambleState[] states = getScrambleStates();
172
    mScrambler = new TwistyObjectScrambler(scramblingType,NUM_AXIS,numLayers,states);
173
174
    boolean bandaged=false;
175
176
    for(int c=0; c<NUM_CUBITS; c++)
177
      {
178
      if( mOrigPos[c].length>3 )
179
        {
180
        bandaged=true;
181
        break;
182
        }
183
      }
184
185
    mIsBandaged = bandaged;
186
187
    mQuatDebug = new int[NUM_CUBITS];
188
189
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
190
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
191
192 e607083f Leszek Koltunski
    mNodeScale= new Static3D(screenWidth,NODE_RATIO*screenWidth,screenWidth);
193 29b82486 Leszek Koltunski
    mQuat = quat;
194
195
    mRotationAngle= new Dynamic1D();
196
    mRotationAxis = new Static3D(1,0,0);
197
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
198
199
    mRotationAngleStatic = new Static1D(0);
200
    mRotationAngleMiddle = new Static1D(0);
201
    mRotationAngleFinal  = new Static1D(0);
202
203
    float scale  = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mRealSize;
204
    mObjectScale = new Static3D(scale,scale,scale);
205
    MatrixEffectScale scaleEffect = new MatrixEffectScale(mObjectScale);
206
    MatrixEffectQuaternion quatEffect  = new MatrixEffectQuaternion(quat, CENTER);
207
208
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
209 d3371e37 Leszek Koltunski
    MatrixEffectMove nodeMoveEffect = new MatrixEffectMove(move);
210
211 29b82486 Leszek Koltunski
    nodeEffects.apply(nodeScaleEffect);
212 d3371e37 Leszek Koltunski
    nodeEffects.apply(nodeMoveEffect);
213 29b82486 Leszek Koltunski
214
    mNumTexCols = NUM_STICKERS_IN_ROW;
215
    mNumTexRows = (NUM_TEXTURES+1)/NUM_STICKERS_IN_ROW;
216
217
    if( mNumTexCols*mNumTexRows < NUM_TEXTURES+1 ) mNumTexRows++;
218
219
    CUBITS = new Cubit[NUM_CUBITS];
220 61aa85e4 Leszek Koltunski
    createMeshAndCubits(res);
221 29b82486 Leszek Koltunski
    createDataStructuresForSolved(numLayers);
222
223
    mTexture = new DistortedTexture();
224
    mEffects = new DistortedEffects();
225
226 9a316d92 Leszek Koltunski
    createTexture();
227
228 29b82486 Leszek Koltunski
    for(int q=0; q<NUM_QUATS; q++)
229
      {
230
      VertexEffectQuaternion vq = new VertexEffectQuaternion(OBJECT_QUATS[q],CENTER);
231
      vq.setMeshAssociation(0,q);
232
      mEffects.apply(vq);
233
      }
234
235
    mEffects.apply(mRotateEffect);
236
    mEffects.apply(quatEffect);
237
    mEffects.apply(scaleEffect);
238
239
    // Now postprocessed effects (the glow when you solve an object) require component centers. In
240
    // order for the effect to be in front of the object, we need to set the center to be behind it.
241
    getMesh().setComponentCenter(0,0,0,-0.1f);
242
243
    attach( new DistortedNode(mTexture,mEffects,mMesh) );
244
245
    float fov = getFOV();
246
    double halfFOV = fov * (Math.PI/360);
247
    mCameraDist = 0.5f*NODE_RATIO / (float)Math.tan(halfFOV);
248
249
    setProjection( fov, 0.1f);
250
    }
251
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253
254
  private Static3D getPos(float[] origPos)
255
    {
256
    int len = origPos.length/3;
257
    float sumX = 0.0f;
258
    float sumY = 0.0f;
259
    float sumZ = 0.0f;
260
261
    for(int i=0; i<len; i++)
262
      {
263
      sumX += origPos[3*i  ];
264
      sumY += origPos[3*i+1];
265
      sumZ += origPos[3*i+2];
266
      }
267
268
    sumX /= len;
269
    sumY /= len;
270
    sumZ /= len;
271
272
    return new Static3D(sumX,sumY,sumZ);
273
    }
274
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
277 61aa85e4 Leszek Koltunski
  private void createMeshAndCubits(Resources res)
278 29b82486 Leszek Koltunski
    {
279 4e1dc313 Leszek Koltunski
    int resourceID= getResource(mNumLayers);
280 29b82486 Leszek Koltunski
281
    if( resourceID!=0 )
282
      {
283
      InputStream is = res.openRawResource(resourceID);
284
      DataInputStream dos = new DataInputStream(is);
285
      mMesh = new MeshFile(dos);
286
287
      try
288
        {
289
        is.close();
290
        }
291
      catch(IOException e)
292
        {
293
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
294
        }
295
296
      for(int i=0; i<NUM_CUBITS; i++)
297
        {
298
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
299
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
300
        }
301
302
      if( shouldResetTextureMaps() ) resetAllTextureMaps();
303
      }
304
    else
305
      {
306
      MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
307
308
      for(int i=0; i<NUM_CUBITS; i++)
309
        {
310
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
311
        cubitMesh[i] = createCubitMesh(i,mNumLayers);
312
        Static3D pos = getPos(mOrigPos[i]);
313
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
314
        cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
315
        }
316
317
      mMesh = new MeshJoined(cubitMesh);
318
      resetAllTextureMaps();
319
      }
320
    }
321
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323
324
  private MeshBase createCubitMesh(int cubit, int numLayers)
325
    {
326
    int variant = getCubitVariant(cubit,numLayers);
327
328
    if( mMeshes==null )
329
      {
330
      FactoryCubit factory = FactoryCubit.getInstance();
331
      factory.clear();
332
      mMeshes = new MeshBase[getNumCubitVariants(numLayers)];
333
      }
334
335
    if( mMeshes[variant]==null )
336
      {
337
      ObjectShape shape = getObjectShape(cubit,numLayers);
338
      FactoryCubit factory = FactoryCubit.getInstance();
339
      factory.createNewFaceTransform(shape);
340
      mMeshes[variant] = factory.createRoundedSolid(shape);
341
      }
342
343
    MeshBase mesh = mMeshes[variant].copy(true);
344
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( getQuat(cubit,numLayers), new Static3D(0,0,0) );
345
    mesh.apply(quat,0xffffffff,0);
346
347
    return mesh;
348
    }
349
350
///////////////////////////////////////////////////////////////////////////////////////////////////
351
352
  private void createDataStructuresForSolved(int numLayers)
353
    {
354
    mTmpQuats = new int[NUM_QUATS];
355
    mSolvedQuats = new int[NUM_CUBITS][];
356
357
    for(int c=0; c<NUM_CUBITS; c++)
358
      {
359
      mSolvedQuats[c] = getSolvedQuats(c,numLayers);
360
      }
361
    }
362
363 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
364
365
  private int getMultQuat(int index1, int index2)
366
    {
367
    if( mQuatMult==null )
368
      {
369
      mQuatMult = new int[NUM_QUATS][NUM_QUATS];
370
371
      for(int i=0; i<NUM_QUATS; i++)
372
        for(int j=0; j<NUM_QUATS; j++) mQuatMult[i][j] = -1;
373
      }
374
375
    if( mQuatMult[index1][index2]==-1 )
376
      {
377
      mQuatMult[index1][index2] = mulQuat(index1,index2);
378
      }
379
380
    return mQuatMult[index1][index2];
381
    }
382
383 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
384
// This is used to build internal data structures for the generic 'isSolved()'
385
//
386
// if this is an internal cubit (all faces black): return -1
387
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
388
// Color index, i.e. the index into the 'FACE_COLORS' table.
389
// else (edge or corner cubit, more than one non-black face): return -2.
390
391 c8bc83d9 Leszek Koltunski
  protected int retCubitSolvedStatus(int cubit, int numLayers)
392 29b82486 Leszek Koltunski
    {
393
    int numNonBlack=0, nonBlackIndex=-1, color;
394
395
    for(int face=0; face<mNumCubitFaces; face++)
396
      {
397
      color = getFaceColor(cubit,face,numLayers);
398
399
      if( color<NUM_TEXTURES )
400
        {
401
        numNonBlack++;
402
        nonBlackIndex = color%NUM_FACE_COLORS;
403
        }
404
      }
405
406
    if( numNonBlack==0 ) return -1;
407
    if( numNonBlack>=2 ) return -2;
408
409
    return nonBlackIndex;
410
    }
411
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413
414 c8bc83d9 Leszek Koltunski
  protected boolean shouldResetTextureMaps()
415 29b82486 Leszek Koltunski
    {
416
    return false;
417
    }
418
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420
421 c8bc83d9 Leszek Koltunski
  protected int[] buildSolvedQuats(Static3D faceAx, Static4D[] quats)
422 29b82486 Leszek Koltunski
    {
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 c8bc83d9 Leszek Koltunski
  private boolean isSolved0()
467 29b82486 Leszek Koltunski
    {
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 c8bc83d9 Leszek Koltunski
  private boolean isSolved1()
534 29b82486 Leszek Koltunski
    {
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 c8bc83d9 Leszek Koltunski
  private boolean isSolved2()
585 29b82486 Leszek Koltunski
    {
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 c8bc83d9 Leszek Koltunski
  private boolean isSolved3()
609 29b82486 Leszek Koltunski
    {
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 0310ac32 Leszek Koltunski
  protected int getScrambleType()
734 29b82486 Leszek Koltunski
    {
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 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
860
      crashlytics.log("points in RotationAngle: "+pointNum);
861
      return 0;
862
      }
863
    }
864
865
///////////////////////////////////////////////////////////////////////////////////////////////////
866
867
  private void recordQuatsState(String message)
868
    {
869
    StringBuilder quats = new StringBuilder();
870
871
    for(int j=0; j<NUM_CUBITS; j++)
872
      {
873
      quats.append(mQuatDebug[j]);
874
      quats.append(" ");
875
      }
876
877
    String name = intGetObjectType(mNumLayers).name();
878
879
    if( BuildConfig.DEBUG )
880
      {
881
      android.util.Log.e("quats" , quats.toString());
882
      android.util.Log.e("object", name);
883
      }
884
    else
885
      {
886
      Exception ex = new Exception(message);
887
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
888
      crashlytics.setCustomKey("quats" , quats.toString());
889
      crashlytics.setCustomKey("object", name);
890
      crashlytics.recordException(ex);
891
      }
892
    }
893
894 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
895 198c5bf0 Leszek Koltunski
896 880beeea Leszek Koltunski
  void initializeObject(int[][] moves)
897 7c111294 Leszek Koltunski
    {
898
    solve();
899
    setupPosition(moves);
900
    }
901 198c5bf0 Leszek Koltunski
902 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
903 198c5bf0 Leszek Koltunski
904 880beeea Leszek Koltunski
  synchronized void removeRotationNow()
905 7c111294 Leszek Koltunski
    {
906
    float angle = getAngle();
907
    double nearestAngleInRadians = angle*Math.PI/180;
908
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
909
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
910
    float axisX = mAxis[mRotAxis].get0();
911
    float axisY = mAxis[mRotAxis].get1();
912
    float axisZ = mAxis[mRotAxis].get2();
913
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
914 198c5bf0 Leszek Koltunski
915 7c111294 Leszek Koltunski
    mRotationAngle.removeAll();
916
    mRotationAngleStatic.set0(0);
917
918
    for(int i=0; i<NUM_CUBITS; i++)
919
      if( belongsToRotation(i,mRotAxis,mRotRowBitmap) )
920 198c5bf0 Leszek Koltunski
        {
921 7c111294 Leszek Koltunski
        int index = CUBITS[i].removeRotationNow(quat);
922
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
923 198c5bf0 Leszek Koltunski
        }
924
    }
925
926 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
927
928 880beeea Leszek Koltunski
  long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
929 c8bc83d9 Leszek Koltunski
    {
930 7c111294 Leszek Koltunski
    if( wasRotateApplied() )
931
      {
932
      float angle = getAngle();
933
      mRotationAngleStatic.set0(angle);
934
      mRotationAngleFinal.set0(nearestAngleInDegrees);
935
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
936 c8bc83d9 Leszek Koltunski
937 7c111294 Leszek Koltunski
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
938
      mRotationAngle.resetToBeginning();
939
      mRotationAngle.removeAll();
940
      mRotationAngle.add(mRotationAngleStatic);
941
      mRotationAngle.add(mRotationAngleMiddle);
942
      mRotationAngle.add(mRotationAngleFinal);
943
      mRotateEffect.notifyWhenFinished(listener);
944 c8bc83d9 Leszek Koltunski
945 7c111294 Leszek Koltunski
      return mRotateEffect.getID();
946
      }
947 c8bc83d9 Leszek Koltunski
948 7c111294 Leszek Koltunski
    return 0;
949 c8bc83d9 Leszek Koltunski
    }
950
951
///////////////////////////////////////////////////////////////////////////////////////////////////
952
953 880beeea Leszek Koltunski
  synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
954 c8bc83d9 Leszek Koltunski
    {
955 7c111294 Leszek Koltunski
    if( wasRotateApplied() )
956
      {
957
      mRotAxis     = axis;
958
      mRotRowBitmap= computeBitmapFromRow( rowBitmap,axis );
959 c8bc83d9 Leszek Koltunski
960 7c111294 Leszek Koltunski
      mRotationAngleStatic.set0(0.0f);
961
      mRotationAxis.set( mAxis[axis] );
962
      mRotationAngle.setDuration(durationMillis);
963
      mRotationAngle.resetToBeginning();
964
      mRotationAngle.add(new Static1D(0));
965
      mRotationAngle.add(new Static1D(angle));
966
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
967
      mRotateEffect.notifyWhenFinished(listener);
968 c8bc83d9 Leszek Koltunski
969 7c111294 Leszek Koltunski
      return mRotateEffect.getID();
970
      }
971
972
    return 0;
973 c8bc83d9 Leszek Koltunski
    }
974
975 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
976
977 880beeea Leszek Koltunski
  void continueRotation(float angleInDegrees)
978
    {
979
    mRotationAngleStatic.set0(angleInDegrees);
980
    }
981
982
///////////////////////////////////////////////////////////////////////////////////////////////////
983
984
  synchronized void beginNewRotation(int axis, int row )
985
    {
986
    if( axis<0 || axis>=NUM_AXIS )
987
      {
988
      android.util.Log.e("object", "invalid rotation axis: "+axis);
989
      return;
990
      }
991
    if( row<0 || row>=mNumLayers )
992
      {
993
      android.util.Log.e("object", "invalid rotation row: "+row);
994
      return;
995
      }
996
997
    mRotAxis     = axis;
998
    mRotRowBitmap= computeBitmapFromRow( (1<<row),axis );
999
    mRotationAngleStatic.set0(0.0f);
1000
    mRotationAxis.set( mAxis[axis] );
1001
    mRotationAngle.add(mRotationAngleStatic);
1002
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
1003
    }
1004
1005
///////////////////////////////////////////////////////////////////////////////////////////////////
1006
1007
  void setTextureMap(int cubit, int face, int newColor)
1008 29b82486 Leszek Koltunski
    {
1009 7c111294 Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
1010
    final float ratioH = 1.0f/mNumTexRows;
1011
    final Static4D[] maps = new Static4D[mNumCubitFaces];
1012
    int row = (mNumTexRows-1) - newColor/mNumTexCols;
1013
    int col = newColor%mNumTexCols;
1014
1015
    maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1016
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1017 29b82486 Leszek Koltunski
    }
1018
1019
///////////////////////////////////////////////////////////////////////////////////////////////////
1020
1021 880beeea Leszek Koltunski
  void resetAllTextureMaps()
1022 29b82486 Leszek Koltunski
    {
1023 7c111294 Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
1024
    final float ratioH = 1.0f/mNumTexRows;
1025
    int color, row, col;
1026 29b82486 Leszek Koltunski
1027 7c111294 Leszek Koltunski
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
1028 29b82486 Leszek Koltunski
      {
1029 7c111294 Leszek Koltunski
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1030 29b82486 Leszek Koltunski
1031 7c111294 Leszek Koltunski
      for(int cubitface=0; cubitface<mNumCubitFaces; cubitface++)
1032
        {
1033
        color = getFaceColor(cubit,cubitface,mNumLayers);
1034
        row = (mNumTexRows-1) - color/mNumTexCols;
1035
        col = color%mNumTexCols;
1036
        maps[cubitface] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1037
        }
1038 29b82486 Leszek Koltunski
1039 7c111294 Leszek Koltunski
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1040
      }
1041 29b82486 Leszek Koltunski
    }
1042
1043
///////////////////////////////////////////////////////////////////////////////////////////////////
1044
1045 880beeea Leszek Koltunski
  void releaseResources()
1046 29b82486 Leszek Koltunski
    {
1047 7c111294 Leszek Koltunski
    mTexture.markForDeletion();
1048
    mMesh.markForDeletion();
1049
    mEffects.markForDeletion();
1050
1051
    for(int j=0; j<NUM_CUBITS; j++)
1052
      {
1053
      CUBITS[j].releaseResources();
1054
      }
1055 29b82486 Leszek Koltunski
    }
1056
1057
///////////////////////////////////////////////////////////////////////////////////////////////////
1058
1059 880beeea Leszek Koltunski
  synchronized void restorePreferences(SharedPreferences preferences)
1060 29b82486 Leszek Koltunski
    {
1061
    boolean error = false;
1062
1063
    for(int i=0; i<NUM_CUBITS; i++)
1064
      {
1065
      mQuatDebug[i] = CUBITS[i].restorePreferences(preferences);
1066
1067
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<NUM_QUATS)
1068
        {
1069
        CUBITS[i].modifyCurrentPosition(OBJECT_QUATS[mQuatDebug[i]]);
1070
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),mQuatDebug[i]);
1071
        }
1072
      else
1073
        {
1074
        error = true;
1075
        }
1076
      }
1077
1078
    if( error )
1079
      {
1080
      for(int i=0; i<NUM_CUBITS; i++)
1081
        {
1082
        CUBITS[i].solve();
1083
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),0);
1084
        }
1085
      recordQuatsState("Failed to restorePreferences");
1086
      }
1087
    }
1088
1089
///////////////////////////////////////////////////////////////////////////////////////////////////
1090
1091 880beeea Leszek Koltunski
  void savePreferences(SharedPreferences.Editor editor)
1092 29b82486 Leszek Koltunski
    {
1093 7c111294 Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++) CUBITS[i].savePreferences(editor);
1094 29b82486 Leszek Koltunski
    }
1095
1096
///////////////////////////////////////////////////////////////////////////////////////////////////
1097
1098 880beeea Leszek Koltunski
  void recomputeScaleFactor(int scrWidth)
1099 29b82486 Leszek Koltunski
    {
1100 7c111294 Leszek Koltunski
    mNodeScale.set(scrWidth,NODE_RATIO*scrWidth,scrWidth);
1101 29b82486 Leszek Koltunski
    }
1102
1103
///////////////////////////////////////////////////////////////////////////////////////////////////
1104 7c111294 Leszek Koltunski
// the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
1105 29b82486 Leszek Koltunski
1106 880beeea Leszek Koltunski
  void createTexture()
1107 29b82486 Leszek Koltunski
    {
1108 7c111294 Leszek Koltunski
    Bitmap bitmap;
1109 29b82486 Leszek Koltunski
1110 7c111294 Leszek Koltunski
    Paint paint = new Paint();
1111
    bitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
1112
    Canvas canvas = new Canvas(bitmap);
1113 29b82486 Leszek Koltunski
1114 7c111294 Leszek Koltunski
    paint.setAntiAlias(true);
1115
    paint.setTextAlign(Paint.Align.CENTER);
1116
    paint.setStyle(Paint.Style.FILL);
1117
1118
    paint.setColor(COLOR_BLACK);
1119
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
1120
1121
    int face = 0;
1122
    FactorySticker factory = FactorySticker.getInstance();
1123
1124
    for(int row=0; row<mNumTexRows; row++)
1125
      for(int col=0; col<mNumTexCols; col++)
1126 29b82486 Leszek Koltunski
        {
1127 7c111294 Leszek Koltunski
        if( face>=NUM_TEXTURES ) break;
1128
        ObjectSticker sticker = retSticker(face);
1129
        factory.drawRoundedPolygon(canvas, paint, col*TEXTURE_HEIGHT, row*TEXTURE_HEIGHT, getColor(face%NUM_FACE_COLORS), sticker);
1130
        face++;
1131 29b82486 Leszek Koltunski
        }
1132
1133 7c111294 Leszek Koltunski
    if( !mTexture.setTexture(bitmap) )
1134
      {
1135
      int max = DistortedLibrary.getMaxTextureSize();
1136
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
1137
      crashlytics.log("failed to set texture of size "+bitmap.getWidth()+"x"+bitmap.getHeight()+" max is "+max);
1138 29b82486 Leszek Koltunski
      }
1139
    }
1140
1141
///////////////////////////////////////////////////////////////////////////////////////////////////
1142
1143 880beeea Leszek Koltunski
  void setObjectRatio(float sizeChange)
1144 29b82486 Leszek Koltunski
    {
1145 7c111294 Leszek Koltunski
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1146 29b82486 Leszek Koltunski
1147 7c111294 Leszek Koltunski
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1148
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1149
1150
    float scale = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mRealSize;
1151
    mObjectScale.set(scale,scale,scale);
1152 29b82486 Leszek Koltunski
    }
1153
1154
///////////////////////////////////////////////////////////////////////////////////////////////////
1155
1156 880beeea Leszek Koltunski
  float getObjectRatio()
1157 29b82486 Leszek Koltunski
    {
1158 7c111294 Leszek Koltunski
    return mObjectScreenRatio*mInitScreenRatio;
1159
    }
1160 29b82486 Leszek Koltunski
1161 02d80fe6 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1162
1163
  public float getRatio()
1164
    {
1165
    return mObjectScreenRatio;
1166
    }
1167
1168 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1169 29b82486 Leszek Koltunski
1170 880beeea Leszek Koltunski
  boolean isSolved()
1171 7c111294 Leszek Koltunski
    {
1172 880beeea Leszek Koltunski
    if( mSolvedFunctionIndex==0 ) return isSolved0();
1173
    if( mSolvedFunctionIndex==1 ) return isSolved1();
1174
    if( mSolvedFunctionIndex==2 ) return isSolved2();
1175
    if( mSolvedFunctionIndex==3 ) return isSolved3();
1176 7c111294 Leszek Koltunski
1177 880beeea Leszek Koltunski
    return false;
1178 29b82486 Leszek Koltunski
    }
1179
1180
///////////////////////////////////////////////////////////////////////////////////////////////////
1181
1182 880beeea Leszek Koltunski
  int getCubit(float[] point3D)
1183 29b82486 Leszek Koltunski
    {
1184 880beeea Leszek Koltunski
    float dist, minDist = Float.MAX_VALUE;
1185
    int currentBest=-1;
1186
    float multiplier = returnMultiplier();
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 7c111294 Leszek Koltunski
    }
1204 29b82486 Leszek Koltunski
1205 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1206 29b82486 Leszek Koltunski
1207 880beeea Leszek Koltunski
  int computeNearestAngle(int axis, float angle, float speed)
1208 7c111294 Leszek Koltunski
    {
1209 880beeea Leszek Koltunski
    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 7c111294 Leszek Koltunski
    }
1220 29b82486 Leszek Koltunski
1221 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1222
1223 880beeea Leszek Koltunski
  float getCameraDist()
1224 7c111294 Leszek Koltunski
    {
1225 880beeea Leszek Koltunski
    return mCameraDist;
1226 29b82486 Leszek Koltunski
    }
1227
1228 880beeea Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1229
// INTERNAL API - those are called from 'effects' package
1230 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1231
1232 880beeea Leszek Koltunski
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
1233 29b82486 Leszek Koltunski
    {
1234 880beeea Leszek Koltunski
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total);
1235 7c111294 Leszek Koltunski
    }
1236 29b82486 Leszek Koltunski
1237 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1238 29b82486 Leszek Koltunski
1239 880beeea Leszek Koltunski
  public int getNodeSize()
1240 7c111294 Leszek Koltunski
    {
1241 880beeea Leszek Koltunski
    return mNodeSize;
1242 29b82486 Leszek Koltunski
    }
1243
1244
///////////////////////////////////////////////////////////////////////////////////////////////////
1245
1246 880beeea Leszek Koltunski
  public Static4D getRotationQuat()
1247 7c111294 Leszek Koltunski
      {
1248 880beeea Leszek Koltunski
      return mQuat;
1249 7c111294 Leszek Koltunski
      }
1250
1251 2df35810 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1252
1253
  public void apply(Effect effect, int position)
1254
    {
1255
    mEffects.apply(effect, position);
1256
    }
1257
1258
///////////////////////////////////////////////////////////////////////////////////////////////////
1259
1260
  public void remove(long effectID)
1261
    {
1262
    mEffects.abortById(effectID);
1263
    }
1264
1265 880beeea Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1266
// PUBLIC API
1267 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1268
1269 880beeea Leszek Koltunski
  public int getCubitFaceColorIndex(int cubit, int face)
1270 29b82486 Leszek Koltunski
    {
1271 880beeea Leszek Koltunski
    Static4D texMap = mMesh.getTextureMap(NUM_FACE_COLORS*cubit + face);
1272 29b82486 Leszek Koltunski
1273 880beeea Leszek Koltunski
    int x = (int)(texMap.get0()/texMap.get2());
1274
    int y = (int)(texMap.get1()/texMap.get3());
1275 29b82486 Leszek Koltunski
1276 880beeea Leszek Koltunski
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
1277 29b82486 Leszek Koltunski
    }
1278
1279
///////////////////////////////////////////////////////////////////////////////////////////////////
1280
1281 880beeea Leszek Koltunski
  public int getNumLayers()
1282 29b82486 Leszek Koltunski
    {
1283 880beeea Leszek Koltunski
    return mNumLayers;
1284
    }
1285 29b82486 Leszek Koltunski
1286
///////////////////////////////////////////////////////////////////////////////////////////////////
1287
1288 880beeea Leszek Koltunski
  public synchronized void solve()
1289 29b82486 Leszek Koltunski
    {
1290 880beeea Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++)
1291
      {
1292
      CUBITS[i].solve();
1293
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
1294
      }
1295 29b82486 Leszek Koltunski
    }
1296
1297
///////////////////////////////////////////////////////////////////////////////////////////////////
1298
1299 c8bc83d9 Leszek Koltunski
  public ObjectType getObjectType()
1300 29b82486 Leszek Koltunski
    {
1301 c8bc83d9 Leszek Koltunski
    return intGetObjectType(mNumLayers);
1302 29b82486 Leszek Koltunski
    }
1303
1304
///////////////////////////////////////////////////////////////////////////////////////////////////
1305
1306 0310ac32 Leszek Koltunski
  protected abstract int getFOV();
1307
  protected abstract float returnMultiplier();
1308
  protected abstract float getScreenRatio();
1309
  protected abstract int getNumFaceColors();
1310
  protected abstract int getColor(int face);
1311 29b82486 Leszek Koltunski
  protected abstract float[][] getCuts(int numLayers);
1312
  protected abstract int getNumCubitFaces();
1313
  protected abstract Static4D[] getQuats();
1314
  protected abstract float[][] getCubitPositions(int numLayers);
1315
  protected abstract int getCubitVariant(int cubit, int numLayers);
1316
  protected abstract int getNumCubitVariants(int numLayers);
1317
  protected abstract Static4D getQuat(int cubit, int numLayers);
1318
  protected abstract ObjectShape getObjectShape(int cubit, int numLayers);
1319
  protected abstract int[] getSolvedQuats(int cubit, int numLayers);
1320
  protected abstract int getSolvedFunctionIndex();
1321
  protected abstract ScrambleState[] getScrambleStates();
1322
  protected abstract int getNumStickerTypes(int numLayers);
1323
  protected abstract ObjectSticker retSticker(int face);
1324
  protected abstract int getFaceColor(int cubit, int cubitface, int numLayers);
1325 4e1dc313 Leszek Koltunski
  protected abstract int getResource(int mNumLayers);
1326 c8bc83d9 Leszek Koltunski
  protected abstract ObjectType intGetObjectType(int numLayers);
1327 7c111294 Leszek Koltunski
  protected abstract Movement getMovement();
1328 29b82486 Leszek Koltunski
1329
  public abstract Static3D[] getRotationAxis();
1330
  public abstract int[] getBasicAngle();
1331
  public abstract int getObjectName(int numLayers);
1332
  public abstract int getInventor(int numLayers);
1333
  public abstract int getComplexity(int numLayers);
1334
  }