Project

General

Profile

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

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

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