Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObject.java @ 758b028d

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 b80e6524 Leszek Koltunski
    MatrixEffectMove moveEffect = new MatrixEffectMove(move);
208 29b82486 Leszek Koltunski
209
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
210
    nodeEffects.apply(nodeScaleEffect);
211
212
    mNumTexCols = NUM_STICKERS_IN_ROW;
213
    mNumTexRows = (NUM_TEXTURES+1)/NUM_STICKERS_IN_ROW;
214
215
    if( mNumTexCols*mNumTexRows < NUM_TEXTURES+1 ) mNumTexRows++;
216
217
    CUBITS = new Cubit[NUM_CUBITS];
218 61aa85e4 Leszek Koltunski
    createMeshAndCubits(res);
219 29b82486 Leszek Koltunski
    createDataStructuresForSolved(numLayers);
220
221
    mTexture = new DistortedTexture();
222
    mEffects = new DistortedEffects();
223
224 9a316d92 Leszek Koltunski
    createTexture();
225
226 29b82486 Leszek Koltunski
    for(int q=0; q<NUM_QUATS; q++)
227
      {
228
      VertexEffectQuaternion vq = new VertexEffectQuaternion(OBJECT_QUATS[q],CENTER);
229
      vq.setMeshAssociation(0,q);
230
      mEffects.apply(vq);
231
      }
232
233
    mEffects.apply(mRotateEffect);
234
    mEffects.apply(quatEffect);
235
    mEffects.apply(scaleEffect);
236 b80e6524 Leszek Koltunski
    mEffects.apply(moveEffect);
237 29b82486 Leszek Koltunski
238
    // Now postprocessed effects (the glow when you solve an object) require component centers. In
239
    // order for the effect to be in front of the object, we need to set the center to be behind it.
240
    getMesh().setComponentCenter(0,0,0,-0.1f);
241
242
    attach( new DistortedNode(mTexture,mEffects,mMesh) );
243
244
    float fov = getFOV();
245
    double halfFOV = fov * (Math.PI/360);
246
    mCameraDist = 0.5f*NODE_RATIO / (float)Math.tan(halfFOV);
247
248
    setProjection( fov, 0.1f);
249
    }
250
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252
253
  private Static3D getPos(float[] origPos)
254
    {
255
    int len = origPos.length/3;
256
    float sumX = 0.0f;
257
    float sumY = 0.0f;
258
    float sumZ = 0.0f;
259
260
    for(int i=0; i<len; i++)
261
      {
262
      sumX += origPos[3*i  ];
263
      sumY += origPos[3*i+1];
264
      sumZ += origPos[3*i+2];
265
      }
266
267
    sumX /= len;
268
    sumY /= len;
269
    sumZ /= len;
270
271
    return new Static3D(sumX,sumY,sumZ);
272
    }
273
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275
276 61aa85e4 Leszek Koltunski
  private void createMeshAndCubits(Resources res)
277 29b82486 Leszek Koltunski
    {
278 4e1dc313 Leszek Koltunski
    int resourceID= getResource(mNumLayers);
279 29b82486 Leszek Koltunski
280
    if( resourceID!=0 )
281
      {
282
      InputStream is = res.openRawResource(resourceID);
283
      DataInputStream dos = new DataInputStream(is);
284
      mMesh = new MeshFile(dos);
285
286
      try
287
        {
288
        is.close();
289
        }
290
      catch(IOException e)
291
        {
292
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
293
        }
294
295
      for(int i=0; i<NUM_CUBITS; i++)
296
        {
297
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
298
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
299
        }
300
301
      if( shouldResetTextureMaps() ) resetAllTextureMaps();
302
      }
303
    else
304
      {
305
      MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
306
307
      for(int i=0; i<NUM_CUBITS; i++)
308
        {
309
        CUBITS[i] = new Cubit(this,mOrigPos[i], NUM_AXIS);
310
        cubitMesh[i] = createCubitMesh(i,mNumLayers);
311
        Static3D pos = getPos(mOrigPos[i]);
312
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
313
        cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
314
        }
315
316
      mMesh = new MeshJoined(cubitMesh);
317
      resetAllTextureMaps();
318
      }
319
    }
320
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322
323
  private MeshBase createCubitMesh(int cubit, int numLayers)
324
    {
325
    int variant = getCubitVariant(cubit,numLayers);
326
327
    if( mMeshes==null )
328
      {
329
      FactoryCubit factory = FactoryCubit.getInstance();
330
      factory.clear();
331
      mMeshes = new MeshBase[getNumCubitVariants(numLayers)];
332
      }
333
334
    if( mMeshes[variant]==null )
335
      {
336
      ObjectShape shape = getObjectShape(cubit,numLayers);
337
      FactoryCubit factory = FactoryCubit.getInstance();
338
      factory.createNewFaceTransform(shape);
339
      mMeshes[variant] = factory.createRoundedSolid(shape);
340
      }
341
342
    MeshBase mesh = mMeshes[variant].copy(true);
343
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( getQuat(cubit,numLayers), new Static3D(0,0,0) );
344
    mesh.apply(quat,0xffffffff,0);
345
346
    return mesh;
347
    }
348
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350
351
  private void createDataStructuresForSolved(int numLayers)
352
    {
353
    mTmpQuats = new int[NUM_QUATS];
354
    mSolvedQuats = new int[NUM_CUBITS][];
355
356
    for(int c=0; c<NUM_CUBITS; c++)
357
      {
358
      mSolvedQuats[c] = getSolvedQuats(c,numLayers);
359
      }
360
    }
361
362 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
363
364
  private int getMultQuat(int index1, int index2)
365
    {
366
    if( mQuatMult==null )
367
      {
368
      mQuatMult = new int[NUM_QUATS][NUM_QUATS];
369
370
      for(int i=0; i<NUM_QUATS; i++)
371
        for(int j=0; j<NUM_QUATS; j++) mQuatMult[i][j] = -1;
372
      }
373
374
    if( mQuatMult[index1][index2]==-1 )
375
      {
376
      mQuatMult[index1][index2] = mulQuat(index1,index2);
377
      }
378
379
    return mQuatMult[index1][index2];
380
    }
381
382 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
383
// This is used to build internal data structures for the generic 'isSolved()'
384
//
385
// if this is an internal cubit (all faces black): return -1
386
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
387
// Color index, i.e. the index into the 'FACE_COLORS' table.
388
// else (edge or corner cubit, more than one non-black face): return -2.
389
390 c8bc83d9 Leszek Koltunski
  protected int retCubitSolvedStatus(int cubit, int numLayers)
391 29b82486 Leszek Koltunski
    {
392
    int numNonBlack=0, nonBlackIndex=-1, color;
393
394
    for(int face=0; face<mNumCubitFaces; face++)
395
      {
396
      color = getFaceColor(cubit,face,numLayers);
397
398
      if( color<NUM_TEXTURES )
399
        {
400
        numNonBlack++;
401
        nonBlackIndex = color%NUM_FACE_COLORS;
402
        }
403
      }
404
405
    if( numNonBlack==0 ) return -1;
406
    if( numNonBlack>=2 ) return -2;
407
408
    return nonBlackIndex;
409
    }
410
411
///////////////////////////////////////////////////////////////////////////////////////////////////
412
413 c8bc83d9 Leszek Koltunski
  protected boolean shouldResetTextureMaps()
414 29b82486 Leszek Koltunski
    {
415
    return false;
416
    }
417
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419
420 c8bc83d9 Leszek Koltunski
  protected int[] buildSolvedQuats(Static3D faceAx, Static4D[] quats)
421 29b82486 Leszek Koltunski
    {
422
    final float MAXD = 0.0001f;
423
    float x = faceAx.get0();
424
    float y = faceAx.get1();
425
    float z = faceAx.get2();
426
    float a,dx,dy,dz,qx,qy,qz;
427
    Static4D quat;
428
429
    int len = quats.length;
430
    int place = 0;
431
432
    for(int q=1; q<len; q++)
433
      {
434
      quat = quats[q];
435
      qx = quat.get0();
436
      qy = quat.get1();
437
      qz = quat.get2();
438
439
           if( x!=0.0f ) { a = qx/x; }
440
      else if( y!=0.0f ) { a = qy/y; }
441
      else               { a = qz/z; }
442
443
      dx = a*x-qx;
444
      dy = a*y-qy;
445
      dz = a*z-qz;
446
447
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
448
        {
449
        mTmpQuats[place++] = q;
450
        }
451
      }
452
453
    if( place!=0 )
454
      {
455
      int[] ret = new int[place];
456
      System.arraycopy(mTmpQuats,0,ret,0,place);
457
      return ret;
458
      }
459
460
    return null;
461
    }
462
463
///////////////////////////////////////////////////////////////////////////////////////////////////
464
465 c8bc83d9 Leszek Koltunski
  private boolean isSolved0()
466 29b82486 Leszek Koltunski
    {
467
    int len, q1,q = CUBITS[0].mQuatIndex;
468
    int[] solved;
469
    boolean skip;
470
471
    for(int c=1; c<NUM_CUBITS; c++)
472
      {
473
      q1 = CUBITS[c].mQuatIndex;
474
475
      if( q1==q ) continue;
476
477
      skip = false;
478
      solved = mSolvedQuats[c];
479
      len = solved==null ? 0:solved.length;
480
481
      for(int i=0; i<len; i++)
482
        {
483
        if( q1==getMultQuat(q,solved[i]) )
484
          {
485
          skip = true;
486
          break;
487
          }
488
        }
489
490
      if( !skip ) return false;
491
      }
492
493
    return true;
494
    }
495
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497
498
  private int computeScramble(int quatNum, int centerNum)
499
    {
500
    float MAXDIFF = 0.01f;
501
    float[] center= mOrigPos[centerNum];
502
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
503
    Static4D result = QuatHelper.rotateVectorByQuat(sc,OBJECT_QUATS[quatNum]);
504
505
    float x = result.get0();
506
    float y = result.get1();
507
    float z = result.get2();
508
509
    for(int c=0; c<NUM_CUBITS; c++)
510
      {
511
      float[] cent = mOrigPos[c];
512
513
      float qx = cent[0] - x;
514
      float qy = cent[1] - y;
515
      float qz = cent[2] - z;
516
517
      if( qx>-MAXDIFF && qx<MAXDIFF &&
518
          qy>-MAXDIFF && qy<MAXDIFF &&
519
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
520
      }
521
522
    return -1;
523
    }
524
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526
// Dino4 uses this. It is solved if and only if groups of cubits
527
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
528
// or
529
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
530
// are all the same color.
531
532 c8bc83d9 Leszek Koltunski
  private boolean isSolved1()
533 29b82486 Leszek Koltunski
    {
534
    if( mScramble==null )
535
      {
536
      mScramble = new int[NUM_QUATS][NUM_CUBITS];
537
      mColors   = new int[NUM_CUBITS];
538
539
      for(int q=0; q<NUM_QUATS; q++)
540
        for(int c=0; c<NUM_CUBITS; c++) mScramble[q][c] = computeScramble(q,c);
541
      }
542
543
    if( mFaceMap==null )
544
      {
545
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
546
      }
547
548
    for(int c=0; c<NUM_CUBITS; c++)
549
      {
550
      int index = mScramble[CUBITS[c].mQuatIndex][c];
551
      mColors[index] = mFaceMap[c];
552
      }
553
554
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
555
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
556
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
557
558
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
559
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
560
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
561
562
    return false;
563
    }
564
565
///////////////////////////////////////////////////////////////////////////////////////////////////
566
// Dino6 uses this. It is solved if and only if:
567
//
568
// All four 'X' cubits (i.e. those whose longest edge goes along the X axis) are rotated
569
// by the same quaternion qX, similarly all four 'Y' cubits by the same qY and all four 'Z'
570
// by the same qZ, and then either:
571
//
572
// a) qX = qY = qZ
573
// b) qY = qX*Q2 and qZ = qX*Q8  (i.e. swap of WHITE and YELLOW faces)
574
// c) qX = qY*Q2 and qZ = qY*Q10 (i.e. swap of BLUE and GREEN faces)
575
// d) qX = qZ*Q8 and qY = qZ*Q10 (i.e. swap of RED and BROWN faces)
576
//
577
// BUT: cases b), c) and d) are really the same - it's all just a mirror image of the original.
578
//
579
// X cubits: 0, 2, 8, 10
580
// Y cubits: 1, 3, 9, 11
581
// Z cubits: 4, 5, 6, 7
582
583 c8bc83d9 Leszek Koltunski
  private boolean isSolved2()
584 29b82486 Leszek Koltunski
    {
585
    int qX = CUBITS[0].mQuatIndex;
586
    int qY = CUBITS[1].mQuatIndex;
587
    int qZ = CUBITS[4].mQuatIndex;
588
589
    if( CUBITS[2].mQuatIndex != qX || CUBITS[8].mQuatIndex != qX || CUBITS[10].mQuatIndex != qX ||
590
        CUBITS[3].mQuatIndex != qY || CUBITS[9].mQuatIndex != qY || CUBITS[11].mQuatIndex != qY ||
591
        CUBITS[5].mQuatIndex != qZ || CUBITS[6].mQuatIndex != qZ || CUBITS[ 7].mQuatIndex != qZ  )
592
      {
593
      return false;
594
      }
595
596
    return ( qX==qY && qX==qZ ) || ( qY==mulQuat(qX,2) && qZ==mulQuat(qX,8) );
597
    }
598
599
///////////////////////////////////////////////////////////////////////////////////////////////////
600
// Square-2 is solved iff
601
// a) all of its cubits are rotated with the same quat
602
// b) its two 'middle' cubits are rotated with the same quat, the 6 'front' and 6 'back'
603
// edges and corners with this quat multiplied by QUATS[18] (i.e. those are upside down)
604
// and all the 12 left and right edges and corners also with the same quat multiplied by
605
// QUATS[12] - i.e. also upside down.
606
607 c8bc83d9 Leszek Koltunski
  private boolean isSolved3()
608 29b82486 Leszek Koltunski
    {
609
    int index = CUBITS[0].mQuatIndex;
610
611
    if( CUBITS[1].mQuatIndex!=index ) return false;
612
613
    boolean solved = true;
614
615
    for(int i=2; i<NUM_CUBITS; i++)
616
      {
617
      if( CUBITS[i].mQuatIndex!=index )
618
        {
619
        solved = false;
620
        break;
621
        }
622
      }
623
624
    if( solved ) return true;
625
626
    int indexX = mulQuat(index,12);  // QUATS[12] = 180deg (1,0,0)
627
    int indexZ = mulQuat(index,18);  // QUATS[18] = 180deg (0,0,1)
628
629
    for(int i= 2; i<        18; i+=2) if( CUBITS[i].mQuatIndex != indexZ ) return false;
630
    for(int i= 3; i<        18; i+=2) if( CUBITS[i].mQuatIndex != indexX ) return false;
631
    for(int i=18; i<NUM_CUBITS; i+=2) if( CUBITS[i].mQuatIndex != indexX ) return false;
632
    for(int i=19; i<NUM_CUBITS; i+=2) if( CUBITS[i].mQuatIndex != indexZ ) return false;
633
634
    return true;
635
    }
636
637
///////////////////////////////////////////////////////////////////////////////////////////////////
638
639
  int computeRow(float[] pos, int axisIndex)
640
    {
641
    int ret=0;
642
    int len = pos.length / 3;
643
    Static3D axis = mAxis[axisIndex];
644
    float axisX = axis.get0();
645
    float axisY = axis.get1();
646
    float axisZ = axis.get2();
647
    float casted;
648
649
    for(int i=0; i<len; i++)
650
      {
651
      casted = pos[3*i]*axisX + pos[3*i+1]*axisY + pos[3*i+2]*axisZ;
652
      ret |= computeSingleRow(axisIndex,casted);
653
      }
654
655
    return ret;
656
    }
657
658
///////////////////////////////////////////////////////////////////////////////////////////////////
659
660
  private int computeSingleRow(int axisIndex,float casted)
661
    {
662
    int num = mNumCuts[axisIndex];
663
664
    for(int i=0; i<num; i++)
665
      {
666
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
667
      }
668
669
    return (1<<num);
670
    }
671
672
///////////////////////////////////////////////////////////////////////////////////////////////////
673
674
  private boolean wasRotateApplied()
675
    {
676
    return mEffects.exists(mRotateEffect.getID());
677
    }
678
679
///////////////////////////////////////////////////////////////////////////////////////////////////
680
681
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
682
    {
683
    return (CUBITS[cubit].getRotRow(axis) & rowBitmap) != 0;
684
    }
685
686
///////////////////////////////////////////////////////////////////////////////////////////////////
687
// note the minus in front of the sin() - we rotate counterclockwise
688
// when looking towards the direction where the axis increases in values.
689
690
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
691
    {
692
    Static3D axis = mAxis[axisIndex];
693
694
    while( angleInDegrees<0 ) angleInDegrees += 360;
695
    angleInDegrees %= 360;
696
    
697
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
698
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
699
700
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
701
    }
702
703
///////////////////////////////////////////////////////////////////////////////////////////////////
704
705
  private synchronized void setupPosition(int[][] moves)
706
    {
707
    if( moves!=null )
708
      {
709
      Static4D quat;
710
      int index, axis, rowBitmap, angle;
711
      int[] basic = getBasicAngle();
712
713
      for(int[] move: moves)
714
        {
715
        axis     = move[0];
716
        rowBitmap= move[1];
717
        angle    = move[2]*(360/basic[axis]);
718
        quat     = makeQuaternion(axis,angle);
719
720
        for(int j=0; j<NUM_CUBITS; j++)
721
          if( belongsToRotation(j,axis,rowBitmap) )
722
            {
723
            index = CUBITS[j].removeRotationNow(quat);
724
            mMesh.setEffectAssociation(j, CUBITS[j].computeAssociation(),index);
725
            }
726
        }
727
      }
728
    }
729
730
///////////////////////////////////////////////////////////////////////////////////////////////////
731
732 0310ac32 Leszek Koltunski
  protected int getScrambleType()
733 29b82486 Leszek Koltunski
    {
734
    return 0;
735
    }
736
737
///////////////////////////////////////////////////////////////////////////////////////////////////
738
739
  int computeBitmapFromRow(int rowBitmap, int axis)
740
    {
741
    if( mIsBandaged )
742
      {
743
      int bitmap, initBitmap=0;
744
745
      while( initBitmap!=rowBitmap )
746
        {
747
        initBitmap = rowBitmap;
748
749
        for(int cubit=0; cubit<NUM_CUBITS; cubit++)
750
          {
751
          bitmap = CUBITS[cubit].getRotRow(axis);
752
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
753
          }
754
        }
755
      }
756
757
    return rowBitmap;
758
    }
759
760
///////////////////////////////////////////////////////////////////////////////////////////////////
761
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
762
// Do so only if minimal Error is appropriately low (shape-shifting puzzles - Square-1)
763
764
  void clampPos(float[] pos, int offset)
765
    {
766
    float currError, minError = Float.MAX_VALUE;
767
    int minErrorIndex1 = -1;
768
    int minErrorIndex2 = -1;
769
770
    float x = pos[offset  ];
771
    float y = pos[offset+1];
772
    float z = pos[offset+2];
773
774
    float xo,yo,zo;
775
776
    for(int i=0; i<NUM_CUBITS; i++)
777
      {
778
      int len = mOrigPos[i].length / 3;
779
780
      for(int j=0; j<len; j++)
781
        {
782
        xo = mOrigPos[i][3*j  ];
783
        yo = mOrigPos[i][3*j+1];
784
        zo = mOrigPos[i][3*j+2];
785
786
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
787
788
        if( currError<minError )
789
          {
790
          minError = currError;
791
          minErrorIndex1 = i;
792
          minErrorIndex2 = j;
793
          }
794
        }
795
      }
796
797
    if( minError< 0.1f ) // TODO: 0.1 ?
798
      {
799
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
800
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
801
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
802
      }
803
    }
804
805
///////////////////////////////////////////////////////////////////////////////////////////////////
806
// remember about the double cover or unit quaternions!
807
808
  int mulQuat(int q1, int q2)
809
    {
810
    Static4D result = QuatHelper.quatMultiply(OBJECT_QUATS[q1],OBJECT_QUATS[q2]);
811
812
    float rX = result.get0();
813
    float rY = result.get1();
814
    float rZ = result.get2();
815
    float rW = result.get3();
816
817
    final float MAX_ERROR = 0.1f;
818
    float dX,dY,dZ,dW;
819
820
    for(int i=0; i<NUM_QUATS; i++)
821
      {
822
      dX = OBJECT_QUATS[i].get0() - rX;
823
      dY = OBJECT_QUATS[i].get1() - rY;
824
      dZ = OBJECT_QUATS[i].get2() - rZ;
825
      dW = OBJECT_QUATS[i].get3() - rW;
826
827
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
828
          dY<MAX_ERROR && dY>-MAX_ERROR &&
829
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
830
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
831
832
      dX = OBJECT_QUATS[i].get0() + rX;
833
      dY = OBJECT_QUATS[i].get1() + rY;
834
      dZ = OBJECT_QUATS[i].get2() + rZ;
835
      dW = OBJECT_QUATS[i].get3() + rW;
836
837
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
838
          dY<MAX_ERROR && dY>-MAX_ERROR &&
839
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
840
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
841
      }
842
843
    return -1;
844
    }
845
846 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
847
848
  private float getAngle()
849
    {
850
    int pointNum = mRotationAngle.getNumPoints();
851
852
    if( pointNum>=1 )
853
      {
854
      return mRotationAngle.getPoint(pointNum-1).get0();
855
      }
856
    else
857
      {
858
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
859
      crashlytics.log("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
878
    if( BuildConfig.DEBUG )
879
      {
880
      android.util.Log.e("quats" , quats.toString());
881
      android.util.Log.e("object", name);
882
      }
883
    else
884
      {
885
      Exception ex = new Exception(message);
886
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
887
      crashlytics.setCustomKey("quats" , quats.toString());
888
      crashlytics.setCustomKey("object", name);
889
      crashlytics.recordException(ex);
890
      }
891
    }
892
893 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
894 198c5bf0 Leszek Koltunski
895 880beeea Leszek Koltunski
  void initializeObject(int[][] moves)
896 7c111294 Leszek Koltunski
    {
897
    solve();
898
    setupPosition(moves);
899
    }
900 198c5bf0 Leszek Koltunski
901 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
902 198c5bf0 Leszek Koltunski
903 880beeea Leszek Koltunski
  synchronized void removeRotationNow()
904 7c111294 Leszek Koltunski
    {
905
    float angle = getAngle();
906
    double nearestAngleInRadians = angle*Math.PI/180;
907
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
908
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
909
    float axisX = mAxis[mRotAxis].get0();
910
    float axisY = mAxis[mRotAxis].get1();
911
    float axisZ = mAxis[mRotAxis].get2();
912
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
913 198c5bf0 Leszek Koltunski
914 7c111294 Leszek Koltunski
    mRotationAngle.removeAll();
915
    mRotationAngleStatic.set0(0);
916
917
    for(int i=0; i<NUM_CUBITS; i++)
918
      if( belongsToRotation(i,mRotAxis,mRotRowBitmap) )
919 198c5bf0 Leszek Koltunski
        {
920 7c111294 Leszek Koltunski
        int index = CUBITS[i].removeRotationNow(quat);
921
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
922 198c5bf0 Leszek Koltunski
        }
923
    }
924
925 c8bc83d9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
926
927 880beeea Leszek Koltunski
  long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
928 c8bc83d9 Leszek Koltunski
    {
929 7c111294 Leszek Koltunski
    if( wasRotateApplied() )
930
      {
931
      float angle = getAngle();
932
      mRotationAngleStatic.set0(angle);
933
      mRotationAngleFinal.set0(nearestAngleInDegrees);
934
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
935 c8bc83d9 Leszek Koltunski
936 7c111294 Leszek Koltunski
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
937
      mRotationAngle.resetToBeginning();
938
      mRotationAngle.removeAll();
939
      mRotationAngle.add(mRotationAngleStatic);
940
      mRotationAngle.add(mRotationAngleMiddle);
941
      mRotationAngle.add(mRotationAngleFinal);
942
      mRotateEffect.notifyWhenFinished(listener);
943 c8bc83d9 Leszek Koltunski
944 7c111294 Leszek Koltunski
      return mRotateEffect.getID();
945
      }
946 c8bc83d9 Leszek Koltunski
947 7c111294 Leszek Koltunski
    return 0;
948 c8bc83d9 Leszek Koltunski
    }
949
950
///////////////////////////////////////////////////////////////////////////////////////////////////
951
952 880beeea Leszek Koltunski
  synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
953 c8bc83d9 Leszek Koltunski
    {
954 7c111294 Leszek Koltunski
    if( wasRotateApplied() )
955
      {
956
      mRotAxis     = axis;
957
      mRotRowBitmap= computeBitmapFromRow( rowBitmap,axis );
958 c8bc83d9 Leszek Koltunski
959 7c111294 Leszek Koltunski
      mRotationAngleStatic.set0(0.0f);
960
      mRotationAxis.set( mAxis[axis] );
961
      mRotationAngle.setDuration(durationMillis);
962
      mRotationAngle.resetToBeginning();
963
      mRotationAngle.add(new Static1D(0));
964
      mRotationAngle.add(new Static1D(angle));
965
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
966
      mRotateEffect.notifyWhenFinished(listener);
967 c8bc83d9 Leszek Koltunski
968 7c111294 Leszek Koltunski
      return mRotateEffect.getID();
969
      }
970
971
    return 0;
972 c8bc83d9 Leszek Koltunski
    }
973
974 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
975
976 880beeea Leszek Koltunski
  void continueRotation(float angleInDegrees)
977
    {
978
    mRotationAngleStatic.set0(angleInDegrees);
979
    }
980
981
///////////////////////////////////////////////////////////////////////////////////////////////////
982
983
  synchronized void beginNewRotation(int axis, int row )
984
    {
985
    if( axis<0 || axis>=NUM_AXIS )
986
      {
987
      android.util.Log.e("object", "invalid rotation axis: "+axis);
988
      return;
989
      }
990
    if( row<0 || row>=mNumLayers )
991
      {
992
      android.util.Log.e("object", "invalid rotation row: "+row);
993
      return;
994
      }
995
996
    mRotAxis     = axis;
997
    mRotRowBitmap= computeBitmapFromRow( (1<<row),axis );
998
    mRotationAngleStatic.set0(0.0f);
999
    mRotationAxis.set( mAxis[axis] );
1000
    mRotationAngle.add(mRotationAngleStatic);
1001
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*ObjectType.MAX_OBJECT_SIZE) , -1);
1002
    }
1003
1004
///////////////////////////////////////////////////////////////////////////////////////////////////
1005
1006
  void setTextureMap(int cubit, int face, int newColor)
1007 29b82486 Leszek Koltunski
    {
1008 7c111294 Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
1009
    final float ratioH = 1.0f/mNumTexRows;
1010
    final Static4D[] maps = new Static4D[mNumCubitFaces];
1011
    int row = (mNumTexRows-1) - newColor/mNumTexCols;
1012
    int col = newColor%mNumTexCols;
1013
1014
    maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1015
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1016 29b82486 Leszek Koltunski
    }
1017
1018
///////////////////////////////////////////////////////////////////////////////////////////////////
1019
1020 880beeea Leszek Koltunski
  void resetAllTextureMaps()
1021 29b82486 Leszek Koltunski
    {
1022 7c111294 Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
1023
    final float ratioH = 1.0f/mNumTexRows;
1024
    int color, row, col;
1025 29b82486 Leszek Koltunski
1026 7c111294 Leszek Koltunski
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
1027 29b82486 Leszek Koltunski
      {
1028 7c111294 Leszek Koltunski
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1029 29b82486 Leszek Koltunski
1030 7c111294 Leszek Koltunski
      for(int cubitface=0; cubitface<mNumCubitFaces; cubitface++)
1031
        {
1032
        color = getFaceColor(cubit,cubitface,mNumLayers);
1033
        row = (mNumTexRows-1) - color/mNumTexCols;
1034
        col = color%mNumTexCols;
1035
        maps[cubitface] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1036
        }
1037 29b82486 Leszek Koltunski
1038 7c111294 Leszek Koltunski
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1039
      }
1040 29b82486 Leszek Koltunski
    }
1041
1042
///////////////////////////////////////////////////////////////////////////////////////////////////
1043
1044 880beeea Leszek Koltunski
  void releaseResources()
1045 29b82486 Leszek Koltunski
    {
1046 7c111294 Leszek Koltunski
    mTexture.markForDeletion();
1047
    mMesh.markForDeletion();
1048
    mEffects.markForDeletion();
1049
1050
    for(int j=0; j<NUM_CUBITS; j++)
1051
      {
1052
      CUBITS[j].releaseResources();
1053
      }
1054 29b82486 Leszek Koltunski
    }
1055
1056
///////////////////////////////////////////////////////////////////////////////////////////////////
1057
1058 880beeea Leszek Koltunski
  synchronized void restorePreferences(SharedPreferences preferences)
1059 29b82486 Leszek Koltunski
    {
1060
    boolean error = false;
1061
1062
    for(int i=0; i<NUM_CUBITS; i++)
1063
      {
1064
      mQuatDebug[i] = CUBITS[i].restorePreferences(preferences);
1065
1066
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<NUM_QUATS)
1067
        {
1068
        CUBITS[i].modifyCurrentPosition(OBJECT_QUATS[mQuatDebug[i]]);
1069
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),mQuatDebug[i]);
1070
        }
1071
      else
1072
        {
1073
        error = true;
1074
        }
1075
      }
1076
1077
    if( error )
1078
      {
1079
      for(int i=0; i<NUM_CUBITS; i++)
1080
        {
1081
        CUBITS[i].solve();
1082
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),0);
1083
        }
1084
      recordQuatsState("Failed to restorePreferences");
1085
      }
1086
    }
1087
1088
///////////////////////////////////////////////////////////////////////////////////////////////////
1089
1090 880beeea Leszek Koltunski
  void savePreferences(SharedPreferences.Editor editor)
1091 29b82486 Leszek Koltunski
    {
1092 7c111294 Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++) CUBITS[i].savePreferences(editor);
1093 29b82486 Leszek Koltunski
    }
1094
1095
///////////////////////////////////////////////////////////////////////////////////////////////////
1096
1097 880beeea Leszek Koltunski
  void recomputeScaleFactor(int scrWidth)
1098 29b82486 Leszek Koltunski
    {
1099 7c111294 Leszek Koltunski
    mNodeScale.set(scrWidth,NODE_RATIO*scrWidth,scrWidth);
1100 29b82486 Leszek Koltunski
    }
1101
1102
///////////////////////////////////////////////////////////////////////////////////////////////////
1103 7c111294 Leszek Koltunski
// the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
1104 29b82486 Leszek Koltunski
1105 880beeea Leszek Koltunski
  void createTexture()
1106 29b82486 Leszek Koltunski
    {
1107 7c111294 Leszek Koltunski
    Bitmap bitmap;
1108 29b82486 Leszek Koltunski
1109 7c111294 Leszek Koltunski
    Paint paint = new Paint();
1110
    bitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
1111
    Canvas canvas = new Canvas(bitmap);
1112 29b82486 Leszek Koltunski
1113 7c111294 Leszek Koltunski
    paint.setAntiAlias(true);
1114
    paint.setTextAlign(Paint.Align.CENTER);
1115
    paint.setStyle(Paint.Style.FILL);
1116
1117
    paint.setColor(COLOR_BLACK);
1118
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
1119
1120
    int face = 0;
1121
    FactorySticker factory = FactorySticker.getInstance();
1122
1123
    for(int row=0; row<mNumTexRows; row++)
1124
      for(int col=0; col<mNumTexCols; col++)
1125 29b82486 Leszek Koltunski
        {
1126 7c111294 Leszek Koltunski
        if( face>=NUM_TEXTURES ) break;
1127
        ObjectSticker sticker = retSticker(face);
1128
        factory.drawRoundedPolygon(canvas, paint, col*TEXTURE_HEIGHT, row*TEXTURE_HEIGHT, getColor(face%NUM_FACE_COLORS), sticker);
1129
        face++;
1130 29b82486 Leszek Koltunski
        }
1131
1132 7c111294 Leszek Koltunski
    if( !mTexture.setTexture(bitmap) )
1133
      {
1134
      int max = DistortedLibrary.getMaxTextureSize();
1135
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
1136
      crashlytics.log("failed to set texture of size "+bitmap.getWidth()+"x"+bitmap.getHeight()+" max is "+max);
1137 29b82486 Leszek Koltunski
      }
1138
    }
1139
1140
///////////////////////////////////////////////////////////////////////////////////////////////////
1141
1142 880beeea Leszek Koltunski
  void setObjectRatio(float sizeChange)
1143 29b82486 Leszek Koltunski
    {
1144 7c111294 Leszek Koltunski
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1145 29b82486 Leszek Koltunski
1146 7c111294 Leszek Koltunski
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1147
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1148
1149
    float scale = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mRealSize;
1150
    mObjectScale.set(scale,scale,scale);
1151 29b82486 Leszek Koltunski
    }
1152
1153
///////////////////////////////////////////////////////////////////////////////////////////////////
1154
1155 880beeea Leszek Koltunski
  float getObjectRatio()
1156 29b82486 Leszek Koltunski
    {
1157 7c111294 Leszek Koltunski
    return mObjectScreenRatio*mInitScreenRatio;
1158
    }
1159 29b82486 Leszek Koltunski
1160 02d80fe6 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1161
1162
  public float getRatio()
1163
    {
1164
    return mObjectScreenRatio;
1165
    }
1166
1167 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1168 29b82486 Leszek Koltunski
1169 880beeea Leszek Koltunski
  boolean isSolved()
1170 7c111294 Leszek Koltunski
    {
1171 880beeea Leszek Koltunski
    if( mSolvedFunctionIndex==0 ) return isSolved0();
1172
    if( mSolvedFunctionIndex==1 ) return isSolved1();
1173
    if( mSolvedFunctionIndex==2 ) return isSolved2();
1174
    if( mSolvedFunctionIndex==3 ) return isSolved3();
1175 7c111294 Leszek Koltunski
1176 880beeea Leszek Koltunski
    return false;
1177 29b82486 Leszek Koltunski
    }
1178
1179
///////////////////////////////////////////////////////////////////////////////////////////////////
1180
1181 880beeea Leszek Koltunski
  int getCubit(float[] point3D)
1182 29b82486 Leszek Koltunski
    {
1183 880beeea Leszek Koltunski
    float dist, minDist = Float.MAX_VALUE;
1184
    int currentBest=-1;
1185
    float multiplier = returnMultiplier();
1186
1187
    point3D[0] *= multiplier;
1188
    point3D[1] *= multiplier;
1189
    point3D[2] *= multiplier;
1190
1191
    for(int i=0; i<NUM_CUBITS; i++)
1192
      {
1193
      dist = CUBITS[i].getDistSquared(point3D);
1194
      if( dist<minDist )
1195
        {
1196
        minDist = dist;
1197
        currentBest = i;
1198
        }
1199
      }
1200
1201
    return currentBest;
1202 7c111294 Leszek Koltunski
    }
1203 29b82486 Leszek Koltunski
1204 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1205 29b82486 Leszek Koltunski
1206 880beeea Leszek Koltunski
  int computeNearestAngle(int axis, float angle, float speed)
1207 7c111294 Leszek Koltunski
    {
1208 880beeea Leszek Koltunski
    int[] basicArray = getBasicAngle();
1209
    int basicAngle   = basicArray[axis>=basicArray.length ? 0 : axis];
1210
    int nearestAngle = 360/basicAngle;
1211
1212
    int tmp = (int)((angle+nearestAngle/2)/nearestAngle);
1213
    if( angle< -(nearestAngle*0.5) ) tmp-=1;
1214
1215
    if( tmp!=0 ) return nearestAngle*tmp;
1216
1217
    return speed> 1.2f ? nearestAngle*(angle>0 ? 1:-1) : 0;
1218 7c111294 Leszek Koltunski
    }
1219 29b82486 Leszek Koltunski
1220 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1221
1222 880beeea Leszek Koltunski
  float getCameraDist()
1223 7c111294 Leszek Koltunski
    {
1224 880beeea Leszek Koltunski
    return mCameraDist;
1225 29b82486 Leszek Koltunski
    }
1226
1227 880beeea Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1228
// INTERNAL API - those are called from 'effects' package
1229 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1230
1231 880beeea Leszek Koltunski
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
1232 29b82486 Leszek Koltunski
    {
1233 880beeea Leszek Koltunski
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total);
1234 7c111294 Leszek Koltunski
    }
1235 29b82486 Leszek Koltunski
1236 7c111294 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1237 29b82486 Leszek Koltunski
1238 880beeea Leszek Koltunski
  public int getNodeSize()
1239 7c111294 Leszek Koltunski
    {
1240 880beeea Leszek Koltunski
    return mNodeSize;
1241 29b82486 Leszek Koltunski
    }
1242
1243
///////////////////////////////////////////////////////////////////////////////////////////////////
1244
1245 880beeea Leszek Koltunski
  public Static4D getRotationQuat()
1246 7c111294 Leszek Koltunski
      {
1247 880beeea Leszek Koltunski
      return mQuat;
1248 7c111294 Leszek Koltunski
      }
1249
1250 2df35810 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1251
1252
  public void apply(Effect effect, int position)
1253
    {
1254
    mEffects.apply(effect, position);
1255
    }
1256
1257
///////////////////////////////////////////////////////////////////////////////////////////////////
1258
1259
  public void remove(long effectID)
1260
    {
1261
    mEffects.abortById(effectID);
1262
    }
1263
1264 3e9df6aa Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1265
1266 758b028d Leszek Koltunski
  public MeshBase getObjectMesh()
1267 3e9df6aa Leszek Koltunski
    {
1268
    return mMesh;
1269
    }
1270
1271 758b028d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1272
1273
  public DistortedEffects getObjectEffects()
1274
    {
1275
    return mEffects;
1276
    }
1277
1278 880beeea Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1279
// PUBLIC API
1280 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
1281
1282 880beeea Leszek Koltunski
  public int getCubitFaceColorIndex(int cubit, int face)
1283 29b82486 Leszek Koltunski
    {
1284 880beeea Leszek Koltunski
    Static4D texMap = mMesh.getTextureMap(NUM_FACE_COLORS*cubit + face);
1285 29b82486 Leszek Koltunski
1286 880beeea Leszek Koltunski
    int x = (int)(texMap.get0()/texMap.get2());
1287
    int y = (int)(texMap.get1()/texMap.get3());
1288 29b82486 Leszek Koltunski
1289 880beeea Leszek Koltunski
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
1290 29b82486 Leszek Koltunski
    }
1291
1292
///////////////////////////////////////////////////////////////////////////////////////////////////
1293
1294 880beeea Leszek Koltunski
  public int getNumLayers()
1295 29b82486 Leszek Koltunski
    {
1296 880beeea Leszek Koltunski
    return mNumLayers;
1297
    }
1298 29b82486 Leszek Koltunski
1299
///////////////////////////////////////////////////////////////////////////////////////////////////
1300
1301 880beeea Leszek Koltunski
  public synchronized void solve()
1302 29b82486 Leszek Koltunski
    {
1303 880beeea Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++)
1304
      {
1305
      CUBITS[i].solve();
1306
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
1307
      }
1308 29b82486 Leszek Koltunski
    }
1309
1310
///////////////////////////////////////////////////////////////////////////////////////////////////
1311
1312 c8bc83d9 Leszek Koltunski
  public ObjectType getObjectType()
1313 29b82486 Leszek Koltunski
    {
1314 c8bc83d9 Leszek Koltunski
    return intGetObjectType(mNumLayers);
1315 29b82486 Leszek Koltunski
    }
1316
1317
///////////////////////////////////////////////////////////////////////////////////////////////////
1318
1319 0310ac32 Leszek Koltunski
  protected abstract int getFOV();
1320
  protected abstract float returnMultiplier();
1321
  protected abstract float getScreenRatio();
1322
  protected abstract int getNumFaceColors();
1323
  protected abstract int getColor(int face);
1324 29b82486 Leszek Koltunski
  protected abstract float[][] getCuts(int numLayers);
1325
  protected abstract int getNumCubitFaces();
1326
  protected abstract Static4D[] getQuats();
1327
  protected abstract float[][] getCubitPositions(int numLayers);
1328
  protected abstract int getCubitVariant(int cubit, int numLayers);
1329
  protected abstract int getNumCubitVariants(int numLayers);
1330
  protected abstract Static4D getQuat(int cubit, int numLayers);
1331
  protected abstract ObjectShape getObjectShape(int cubit, int numLayers);
1332
  protected abstract int[] getSolvedQuats(int cubit, int numLayers);
1333
  protected abstract int getSolvedFunctionIndex();
1334
  protected abstract ScrambleState[] getScrambleStates();
1335
  protected abstract int getNumStickerTypes(int numLayers);
1336
  protected abstract ObjectSticker retSticker(int face);
1337
  protected abstract int getFaceColor(int cubit, int cubitface, int numLayers);
1338 4e1dc313 Leszek Koltunski
  protected abstract int getResource(int mNumLayers);
1339 c8bc83d9 Leszek Koltunski
  protected abstract ObjectType intGetObjectType(int numLayers);
1340 7c111294 Leszek Koltunski
  protected abstract Movement getMovement();
1341 29b82486 Leszek Koltunski
1342
  public abstract Static3D[] getRotationAxis();
1343
  public abstract int[] getBasicAngle();
1344
  public abstract int getObjectName(int numLayers);
1345
  public abstract int getInventor(int numLayers);
1346
  public abstract int getComplexity(int numLayers);
1347
  }