Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObject.java @ 28bfa000

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.main;
11

    
12
import java.io.DataInputStream;
13
import java.io.IOException;
14
import java.io.InputStream;
15
import java.util.Random;
16

    
17
import android.content.SharedPreferences;
18
import android.graphics.Bitmap;
19
import android.graphics.Canvas;
20
import android.graphics.Paint;
21

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

    
43
import org.distorted.objectlib.helpers.FactoryCubit;
44
import org.distorted.objectlib.helpers.FactorySticker;
45
import org.distorted.objectlib.helpers.ObjectFaceShape;
46
import org.distorted.objectlib.helpers.ObjectLibInterface;
47
import org.distorted.objectlib.helpers.ObjectShape;
48
import org.distorted.objectlib.helpers.ObjectSignature;
49
import org.distorted.objectlib.helpers.ObjectSticker;
50
import org.distorted.objectlib.helpers.ObjectStickerOverride;
51
import org.distorted.objectlib.helpers.ObjectVertexEffects;
52
import org.distorted.objectlib.helpers.QuatGroupGenerator;
53
import org.distorted.objectlib.scrambling.ObjectScrambler;
54
import org.distorted.objectlib.json.JsonReader;
55
import org.distorted.objectlib.touchcontrol.*;
56

    
57
import static org.distorted.objectlib.touchcontrol.TouchControl.*;
58

    
59
///////////////////////////////////////////////////////////////////////////////////////////////////
60

    
61
public abstract class TwistyObject
62
  {
63
  public static final int MESH_NICE = 0;
64
  public static final int MESH_FAST = 1;
65

    
66
  public static final int MODE_ICON = 0;
67
  public static final int MODE_NORM = 1;
68

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

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

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

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

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

    
94
  protected float[][] mStickerCoords;
95
  protected Static4D[] mObjectQuats;
96

    
97
  private int[][] mStickerVariants;
98
  private float[] mStickerScales;
99
  private Cubit[] mCubits;
100
  private MeshBase[] mMeshes;
101
  private int mNumCubits, mNumQuats, mNumFaceColors, mNumTextures, mNumOverrides;
102
  private int mNumCubitFaces, mNumStickerTypes;
103
  private Static3D[] mAxis;
104
  private float[][] mCuts;
105
  private int[] mNumCuts;
106
  private float[][] mOrigPos;
107
  private Static4D[] mOrigQuat;
108
  private Static4D[] mMixupModeQuats;
109
  private boolean mIsInMixupMode;
110
  private Static4D mQuat;
111
  private int[] mNumLayers;
112
  private float mSize;
113
  private DistortedEffects mEffects;
114
  private VertexEffectRotate mRotateEffect;
115
  private Dynamic1D mRotationAngle;
116
  private Static3D mRotationAxis;
117
  private Static3D mObjectScale;
118
  private int[] mQuatDebug;
119
  private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
120
  private DistortedTexture mTexture;
121
  private float mInitScreenRatio;
122
  private int mSolvedFunctionIndex;
123
  private boolean mIsBandaged;
124
  private float mObjectScreenRatio;
125
  private int[][] mSolvedQuats;
126
  private int[][] mQuatMult;
127
  private int[] mTmpQuats;
128
  private int mNumTexRows, mNumTexCols;
129
  private int mRotRowBitmap;
130
  private int mCurrentRotAxis;
131
  private MeshBase mMesh;
132
  private ObjectScrambler mScrambler;
133
  private TouchControl mTouchControl;
134
  private DistortedNode mNode;
135
  private ObjectLibInterface mInterface;
136
  private Bitmap mBitmap;
137
  private ObjectSticker[] mStickers;
138
  private ObjectShape[] mShapes;
139
  private int mNumCubitVariants;
140
  private int[][] mCubitFaceColors;
141
  private int[][] mVariantFaceIsOuter;
142
  private int[][] mBasicAngles;
143
  private int mIconMode;
144
  private InitData mInitData;
145
  private float[][] mRowOffsets;
146
  private boolean[] mBelongs;
147
  private float[] mTmp;
148
  private int mNumPuzzleFaces;
149
  private ObjectStickerOverride[] mStickerOverrides;
150
  private boolean mError;
151
  private String mErrorString;
152
  private int mMaxNumLayers;
153
  private int mNumAxis;
154

    
155
  //////////////////// SOLVED1 ////////////////////////
156

    
157
  private int[] mFaceMap;
158
  private int[][] mScramble;
159
  private int[] mColors;
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  TwistyObject(InputStream jsonStream, int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream meshStream)
164
    {
165
    try
166
      {
167
      JsonReader reader = new JsonReader();
168
      reader.parseJsonFile(jsonStream);
169
      setReader(reader);
170
      mNumLayers = reader.getNumLayers();
171
      mSize      = reader.getSize();
172
      mInitData  = null;
173
      initialize(meshState,iconMode,quat,move,scale,meshStream,true);
174
      mError = false;
175
      mErrorString=null;
176
      }
177
    catch(Exception ex)
178
      {
179
      mError = true;
180
      mErrorString = ex.getMessage();
181
      }
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

    
186
  public TwistyObject(InitData data, int meshState, int iconMode, float size, Static4D quat, Static3D move, float scale, InputStream meshStream)
187
    {
188
    mNumLayers = data.getNumLayers();
189
    mSize      = size;
190
    mInitData  = data;
191
    initialize(meshState,iconMode,quat,move,scale,meshStream,false);
192
    mError = false;
193
    mErrorString = null;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  private void debugQuat(Static4D quat, int cubitIndex, float axisX, float axisY, float axisZ, float angle, int place)
199
    {
200
    float[] tracking = mCubits[cubitIndex].getTrackingPoint();
201

    
202
    String problem = (getShortName()+" "+cubitIndex+" "+quat.get0()+" "+quat.get1()+" "+quat.get2());
203
    problem += (" "+angle+" "+place+" "+tracking[0]+" "+tracking[1]+" "+tracking[2]);
204
    problem += (axisX+" "+axisY+" "+axisZ);
205

    
206
    mInterface.reportProblem(problem,true);
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210

    
211
  private void initialize(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream meshStream, boolean fromJSON)
212
    {
213
    mIconMode = iconMode;
214
    mQuat = quat;
215
    mAxis = getRotationAxis();
216
    mInitScreenRatio = getScreenRatio();
217
    mSolvedFunctionIndex = getSolvedFunctionIndex();
218
    mBasicAngles = getBasicAngles();
219
    mObjectQuats = getQuats();
220
    mNumQuats = mObjectQuats.length;
221
    mOrigPos = getCubitPositions(mNumLayers);
222
    mNumPuzzleFaces = getNumFaces();
223
    mRowOffsets = new float[mNumPuzzleFaces][3];
224
    mTmp = new float[4];
225

    
226
    mNumAxis = mAxis.length;
227
    mCuts = getCuts(mNumLayers);
228
    mNumCuts = new int[mNumAxis];
229
    mMaxNumLayers = -1;
230
    for(int i=0; i<mNumAxis; i++)
231
      {
232
      if( mMaxNumLayers<mNumLayers[i] ) mMaxNumLayers = mNumLayers[i];
233
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
234
      }
235

    
236
    mNumCubits = mOrigPos.length;
237
    mNumFaceColors = getNumFaceColors();
238
    mBelongs = new boolean[mNumCubits];
239

    
240
    int scramblingType = getScrambleType();
241
    int[][] edges = getScrambleEdges();
242
    int[][] algorithms = getScrambleAlgorithms();
243

    
244
    // if( edges!=null )      print_table("EDGES", edges);
245
    // if( algorithms!=null ) print_table("ALGOS", algorithms);
246

    
247
    mScrambler = new ObjectScrambler(scramblingType,mNumAxis,mNumLayers,algorithms,edges);
248

    
249
    boolean bandaged=false;
250

    
251
    for( int c=0; c<mNumCubits; c++)
252
      {
253
      if( mOrigPos[c].length>3 )
254
        {
255
        bandaged=true;
256
        break;
257
        }
258
      }
259
    mIsBandaged = bandaged;
260
    mQuatDebug = new int[mNumCubits];
261

    
262
    mRotationAngle= new Dynamic1D();
263
    mRotationAxis = new Static3D(1,0,0);
264
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
265

    
266
    mRotationAngleStatic = new Static1D(0);
267
    mRotationAngleMiddle = new Static1D(0);
268
    mRotationAngleFinal  = new Static1D(0);
269

    
270
    mObjectScale = new Static3D(scale,scale,scale);
271
    setObjectRatioNow(scale,720);
272

    
273
    mEffects = new DistortedEffects();
274
    createQuaternionEffects();
275

    
276
    MatrixEffectScale scaleEffect = new MatrixEffectScale(mObjectScale);
277
    MatrixEffectQuaternion quatEffect = new MatrixEffectQuaternion(mQuat, CENTER);
278
    MatrixEffectMove moveEffect = new MatrixEffectMove(move);
279

    
280
    boolean fromDMESH = (meshStream!=null && meshState==MESH_NICE);
281
    getQuatsAndShapes(fromDMESH,fromJSON);
282
    createMeshAndCubits(meshStream,meshState,fromDMESH);
283
    setUpTextures(fromDMESH,fromJSON);
284
    createDataStructuresForSolved();
285

    
286
    mEffects.apply(mRotateEffect);
287
    mEffects.apply(quatEffect);
288
    mEffects.apply(scaleEffect);
289
    mEffects.apply(moveEffect);
290

    
291
    mNode = new DistortedNode(mTexture,mEffects,mMesh);
292
    }
293

    
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295

    
296
  private static void print_table(String mess, int[][] table)
297
    {
298
    android.util.Log.e("D", mess);
299

    
300
    int len = table.length;
301

    
302
    for(int i=0; i<len; i++)
303
      {
304
      String m = "";
305
      int l = table[i].length;
306
      for(int j=0; j<l; j++) m += (" "+table[i][j]);
307
      android.util.Log.e("D", m);
308
      }
309
    }
310

    
311
///////////////////////////////////////////////////////////////////////////////////////////////////
312

    
313
  public static int[][] staticGetAlgorithms(int[][] basicAngles)
314
    {
315
    int numAxis= basicAngles.length;
316
    int num=0;
317

    
318
    for (int[] basic : basicAngles)
319
      for (int i : basic) num += (i-1);
320

    
321
    int[][] ret = new int[num][3];
322
    int index = 0;
323

    
324
    for(int i=0; i<numAxis; i++)
325
      {
326
      int numLayers = basicAngles[i].length;
327

    
328
      for(int j=0; j<numLayers; j++)
329
        {
330
        int N = basicAngles[i][j];
331
        int NEG = (1-N)/2;
332
        int POS = N/2;
333

    
334
        for(int k=NEG; k<=-1; k++)
335
          {
336
          ret[index][0] = i;
337
          ret[index][1] = j;
338
          ret[index][2] = k;
339
          index++;
340
          }
341

    
342
        for(int k=1; k<=POS; k++)
343
          {
344
          ret[index][0] = i;
345
          ret[index][1] = j;
346
          ret[index][2] = k;
347
          index++;
348
          }
349
        }
350
      }
351

    
352
    return ret;
353
    }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356

    
357
  private void createQuaternionEffects()
358
    {
359
    if( mNumQuats<=ObjectControl.MAX_QUATS )
360
      {
361
      mIsInMixupMode = false;
362

    
363
      for( int q=0; q<mNumQuats; q++)
364
        {
365
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mObjectQuats[q],CENTER);
366
        vq.setMeshAssociation(0,q);
367
        mEffects.apply(vq);
368
        }
369
      }
370
    else if( mNumCubits<=ObjectControl.MAX_QUATS )
371
      {
372
      mIsInMixupMode = true;
373
      mMixupModeQuats = new Static4D[mNumCubits];
374

    
375
      for( int q=0; q<mNumCubits; q++)
376
        {
377
        mMixupModeQuats[q] = new Static4D(mObjectQuats[0]);
378
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mMixupModeQuats[q],CENTER);
379
        vq.setMeshAssociation(0,q);
380
        mEffects.apply(vq);
381
        }
382
      }
383
    else
384
      {
385
      android.util.Log.e("D", "object has too many quaternions ("+mNumQuats+") or too many cubits ("+mNumCubits+")");
386
      }
387
    }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390

    
391
  private Static3D getPos(float[] origPos)
392
    {
393
    int len = origPos.length/3;
394
    float sumX = 0.0f;
395
    float sumY = 0.0f;
396
    float sumZ = 0.0f;
397

    
398
    for(int i=0; i<len; i++)
399
      {
400
      sumX += origPos[3*i  ];
401
      sumY += origPos[3*i+1];
402
      sumZ += origPos[3*i+2];
403
      }
404

    
405
    sumX /= len;
406
    sumY /= len;
407
    sumZ /= len;
408

    
409
    return new Static3D(sumX,sumY,sumZ);
410
    }
411

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

    
414
  private void createOuterFaces()
415
    {
416
    for(int v=0; v<mNumCubitVariants; v++)
417
      {
418
      int[][] indices = mShapes[v].getVertIndices();
419
      int faces = indices.length;
420
      mVariantFaceIsOuter[v] = new int[faces];
421
      }
422

    
423
    for( int cubit=0; cubit<mNumCubits; cubit++)
424
      {
425
      int variant = getCubitVariant(cubit,mNumLayers);
426
      int[][] indices = mShapes[variant].getVertIndices();
427
      int numFaces = indices.length;
428

    
429
      for(int face=0; face<numFaces; face++)
430
        if( getCubitFaceColor(cubit,face)>=0 )
431
          {
432
          mVariantFaceIsOuter[variant][face] = 1;
433
          }
434
      }
435
    }
436

    
437
///////////////////////////////////////////////////////////////////////////////////////////////////
438

    
439
  private void getQuatsAndShapes(boolean fromDMESH, boolean fromJSON)
440
    {
441
    mNumCubitVariants = getNumCubitVariants(mNumLayers);
442

    
443
    if( !fromDMESH || !fromJSON )
444
      {
445
      FactoryCubit factory = FactoryCubit.getInstance();
446
      factory.clear();
447

    
448
      mOrigQuat = new Static4D[mNumCubits];
449
      for(int i=0; i<mNumCubits; i++) mOrigQuat[i] = getCubitQuats(i,mNumLayers);
450

    
451
      mShapes = new ObjectShape[mNumCubitVariants];
452
      for(int i=0; i<mNumCubitVariants; i++) mShapes[i] = getObjectShape(i);
453
      mNumCubitFaces = ObjectShape.computeNumComponents(mShapes);
454
      mVariantFaceIsOuter = new int[mNumCubitVariants][];
455

    
456
      if( !fromJSON )
457
        {
458
        mCubitFaceColors = ObjectShape.computeColors(mShapes,mOrigPos,mOrigQuat,this);
459
        createOuterFaces();
460
        }
461

    
462
      if( fromDMESH )
463
        {
464
        for(int i=0; i<mNumCubitVariants; i++) factory.createNewFaceTransform(mShapes[i], mVariantFaceIsOuter[i]);
465
        }
466
      }
467
    }
468

    
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470

    
471
  private void createMeshAndCubits(InputStream stream, int meshState, boolean fromDMESH)
472
    {
473
    mCubits = new Cubit[mNumCubits];
474

    
475
    if( fromDMESH )
476
      {
477
      DataInputStream dos = new DataInputStream(stream);
478
      mMesh = new MeshFile(dos);
479

    
480
      try
481
        {
482
        stream.close();
483
        }
484
      catch(IOException e)
485
        {
486
        android.util.Log.e("meshFile", "Error closing InputStream: "+e);
487
        }
488
      }
489
    else
490
      {
491
      MeshBase[] cubitMesh = new MeshBase[mNumCubits];
492

    
493
      for(int i=0; i<mNumCubits; i++)
494
        {
495
        cubitMesh[i] = createCubitMesh(i,mNumLayers,meshState,mNumCubitFaces);
496
        Static3D pos = getPos(mOrigPos[i]);
497
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
498
        }
499

    
500
      mMesh = new MeshJoined(cubitMesh);
501

    
502
      float pillowCoeff = getPillowCoeff();
503

    
504
      if( pillowCoeff!=1.0f )
505
        {
506
        float radius = getCircumscribedRadius();
507
        Static1D coeff = new Static1D(pillowCoeff);
508
        Static4D region= new Static4D(0,0,0,radius);
509
        VertexEffectSink sink = new VertexEffectSink(coeff,CENTER,region);
510
        mMesh.apply(sink);
511
        }
512
      }
513

    
514
    for(int i=0; i<mNumCubits; i++)
515
      {
516
      mCubits[i] = new Cubit(this,mOrigPos[i],mNumAxis,mMaxNumLayers,i);
517
      setCubitQuat(i,mCubits[i].computeAssociation(),0);
518
      }
519
    }
520

    
521
///////////////////////////////////////////////////////////////////////////////////////////////////
522

    
523
  private MeshBase createCubitMesh(int cubit, int[] numLayers, int meshState, int numComponents)
524
    {
525
    int variant = getCubitVariant(cubit,numLayers);
526

    
527
    if( mMeshes==null ) mMeshes = new MeshBase[mNumCubitVariants];
528

    
529
    if( mMeshes[variant]==null )
530
      {
531
      ObjectFaceShape faceShape = getObjectFaceShape(variant);
532
      ObjectVertexEffects effects = getVertexEffects(variant);
533
      FactoryCubit factory = FactoryCubit.getInstance();
534
      factory.createNewFaceTransform(mShapes[variant],mVariantFaceIsOuter[variant]);
535
      mMeshes[variant] = factory.createRoundedSolid(mShapes[variant],faceShape,effects, meshState, numComponents);
536
      }
537

    
538
    MeshBase mesh = mMeshes[variant].copy(true);
539
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( mOrigQuat[cubit], CENTER );
540
    mesh.apply(quat,0xffffffff,0);
541

    
542
    return mesh;
543
    }
544

    
545
///////////////////////////////////////////////////////////////////////////////////////////////////
546

    
547
  private void setUpTextures(boolean fromDMESH, boolean fromJSON)
548
    {
549
    mTexture = new DistortedTexture();
550

    
551
    if( fromJSON )
552
      {
553
      mNumStickerTypes = getNumStickerTypes();
554
      mNumCubitFaces = getNumCubitFaces();
555
      }
556
    else
557
      {
558
      FactoryCubit factory = FactoryCubit.getInstance();
559
      mStickerCoords   = factory.getStickerCoords();
560
      mStickerVariants = factory.getStickerVariants();
561
      mStickerScales   = factory.getStickerScales();
562
      adjustStickerCoords();
563
      mNumStickerTypes = (mStickerCoords==null ? 0 : mStickerCoords.length);
564
      }
565

    
566
    mStickerOverrides = getStickerOverrides();
567
    mNumOverrides = mStickerOverrides==null ? 0 : mStickerOverrides.length;
568

    
569
    mNumTextures= mNumFaceColors*mNumStickerTypes + mNumOverrides;
570
    mNumTexCols = NUM_STICKERS_IN_ROW;
571
    mNumTexRows = (mNumTextures+1)/NUM_STICKERS_IN_ROW;
572
    if( mNumTexCols*mNumTexRows < mNumTextures+1 ) mNumTexRows++;
573

    
574
    if( !fromDMESH || shouldResetTextureMaps() ) resetAllTextureMaps();
575
    else overrideCubitFaceColor();
576

    
577
    setTexture();
578
    }
579

    
580
///////////////////////////////////////////////////////////////////////////////////////////////////
581

    
582
  private int getMultQuat(int index1, int index2)
583
    {
584
    if( mQuatMult==null )
585
      {
586
      mQuatMult = new int[mNumQuats][mNumQuats];
587

    
588
      for(int i=0; i<mNumQuats; i++)
589
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
590
      }
591

    
592
    if( index1<mNumQuats && index2<mNumQuats )
593
      {
594
      if( mQuatMult[index1][index2]==-1 ) mQuatMult[index1][index2] = mulQuat(index1,index2);
595
      return mQuatMult[index1][index2];
596
      }
597

    
598
    return -1;
599
    }
600

    
601
///////////////////////////////////////////////////////////////////////////////////////////////////
602

    
603
  public InitData getInitData()
604
    {
605
    return mInitData;
606
    }
607

    
608
///////////////////////////////////////////////////////////////////////////////////////////////////
609

    
610
  public boolean isInIconMode()
611
    {
612
    return mIconMode==MODE_ICON;
613
    }
614

    
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616

    
617
  public int getVariantStickerShape(int variant, int face)
618
    {
619
    return face>=mStickerVariants[variant].length ? -1 : mStickerVariants[variant][face];
620
    }
621

    
622
///////////////////////////////////////////////////////////////////////////////////////////////////
623

    
624
  public boolean shouldResetTextureMaps()
625
    {
626
    return false;
627
    }
628

    
629
///////////////////////////////////////////////////////////////////////////////////////////////////
630

    
631
  public int[][] getScrambleAlgorithms()
632
    {
633
    return staticGetAlgorithms(mBasicAngles);
634
    }
635

    
636
///////////////////////////////////////////////////////////////////////////////////////////////////
637

    
638
  private void createDataStructuresForSolved()
639
    {
640
    mTmpQuats = new int[mNumQuats];
641
    mSolvedQuats = getSolvedQuats();
642
    }
643

    
644
///////////////////////////////////////////////////////////////////////////////////////////////////
645
// This is used to build internal data structures for the generic 'isSolved()'
646
//
647
// if this is an internal cubit (all faces black): return -1
648
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
649
// Color index, i.e. the index into the 'FACE_COLORS' table.
650
// else (edge or corner cubit, more than one non-black face): return -2.
651

    
652
  protected int retCubitSolvedStatus(int cubit)
653
    {
654
    int numNonBlack=0, nonBlackIndex=-1, stiShape, cubColor;
655
    int variant = getCubitVariant(cubit,mNumLayers);
656

    
657
    for(int face=0; face<mNumCubitFaces; face++)
658
      {
659
      stiShape = getVariantStickerShape(variant,face);
660
      int numFaces = mCubitFaceColors[cubit].length;
661
      cubColor = face<numFaces ? mCubitFaceColors[cubit][face] : -1;
662

    
663
      if( stiShape>=0 && cubColor>=0 )
664
        {
665
        numNonBlack++;
666
        nonBlackIndex = cubColor;
667
        }
668
      }
669

    
670
    if( numNonBlack==0 ) return -1;
671
    if( numNonBlack>=2 ) return -2;
672

    
673
    return nonBlackIndex;
674
    }
675

    
676
///////////////////////////////////////////////////////////////////////////////////////////////////
677

    
678
  private boolean sticksOut(Static3D[] faceAxis, float[] dist, float x, float y, float z )
679
    {
680
    final float MAXERR = 0.05f;
681
    int numAxis = dist.length;
682

    
683
    for(int i=0; i<numAxis; i++)
684
      {
685
      Static3D ax = faceAxis[i];
686
      float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
687
      if( len>mSize*dist[i]+MAXERR ) return true;
688
      }
689

    
690
    return false;
691
    }
692

    
693
///////////////////////////////////////////////////////////////////////////////////////////////////
694

    
695
  private boolean doesNotStickOut(int variant, float px, float py, float pz, float[] tmp, Static4D quat)
696
    {
697
    float[][] vertices = mShapes[variant].getVertices();
698
    Static3D[] axis = getFaceAxis();
699
    float[] dist3D = getDist3D(mNumLayers);
700

    
701
    for( float[] vertex : vertices)
702
      {
703
      float x = vertex[0];
704
      float y = vertex[1];
705
      float z = vertex[2];
706

    
707
      QuatHelper.rotateVectorByQuat(tmp, x, y, z, 1, quat);
708

    
709
      float mx = tmp[0] + px;
710
      float my = tmp[1] + py;
711
      float mz = tmp[2] + pz;
712

    
713
      if( sticksOut(axis, dist3D, mx,my,mz) ) return false;
714
      }
715

    
716
    return true;
717
    }
718

    
719
///////////////////////////////////////////////////////////////////////////////////////////////////
720

    
721
  private float computeAvg(float[] pos, int offset)
722
    {
723
    int len = pos.length/3;
724
    float ret=0.0f;
725
    for(int i=0; i<len; i++) ret += pos[3*i+offset];
726
    ret /= len;
727

    
728
    return ret;
729
    }
730

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

    
733
  protected void displayCubitQuats()
734
    {
735
    StringBuilder builder = new StringBuilder();
736
    float[] tmp = new float[4];
737
    float ERR = 0.01f;
738

    
739
    for(int cubit=0; cubit<mNumCubits; cubit++)
740
      {
741
      builder.append(cubit);
742
      builder.append(" : ");
743

    
744
      int refCubit,variant = getCubitVariant(cubit,mNumLayers);
745

    
746
      for(refCubit=0; refCubit<mNumCubits; refCubit++)
747
        {
748
        if( getCubitVariant(refCubit,mNumLayers)==variant ) break;
749
        }
750

    
751
      float[] curpos = mOrigPos[cubit];
752
      float[] refpos = mOrigPos[refCubit];
753
      float refX = computeAvg(refpos,0);
754
      float refY = computeAvg(refpos,1);
755
      float refZ = computeAvg(refpos,2);
756
      float curX = computeAvg(curpos,0);
757
      float curY = computeAvg(curpos,1);
758
      float curZ = computeAvg(curpos,2);
759

    
760
      for(int quat=0; quat<mNumQuats; quat++)
761
        {
762
        QuatHelper.rotateVectorByQuat(tmp,refX,refY,refZ,0,mObjectQuats[quat]);
763

    
764
        float dx = tmp[0]-curX;
765
        float dy = tmp[1]-curY;
766
        float dz = tmp[2]-curZ;
767

    
768
        if( dx>-ERR && dx<ERR && dy>-ERR && dy<ERR && dz>-ERR && dz<ERR )
769
          {
770
          if( doesNotStickOut(variant,curX,curY,curZ,tmp,mObjectQuats[quat]) )
771
            {
772
            builder.append(quat);
773
            builder.append(',');
774
            }
775
          else
776
            {
777
            android.util.Log.e("D", "cubit: "+cubit+" quat: "+quat+" : center correct, but shape sticks out");
778
            }
779
          }
780
        }
781

    
782
      builder.append('\n');
783
      }
784

    
785
    android.util.Log.e("D", "cubitQuats: \n"+builder );
786
    }
787

    
788
///////////////////////////////////////////////////////////////////////////////////////////////////
789

    
790
  protected int[] buildSolvedQuats(Static3D faceAx)
791
    {
792
    final float MAXD = 0.0001f;
793
    float x = faceAx.get0();
794
    float y = faceAx.get1();
795
    float z = faceAx.get2();
796
    float a,dx,dy,dz,qx,qy,qz;
797
    Static4D quat;
798
    int place = 0;
799

    
800
    for(int q=1; q<mNumQuats; q++)
801
      {
802
      quat = mObjectQuats[q];
803
      qx = quat.get0();
804
      qy = quat.get1();
805
      qz = quat.get2();
806

    
807
           if( x!=0.0f ) { a = qx/x; }
808
      else if( y!=0.0f ) { a = qy/y; }
809
      else               { a = qz/z; }
810

    
811
      dx = a*x-qx;
812
      dy = a*y-qy;
813
      dz = a*z-qz;
814

    
815
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
816
        {
817
        mTmpQuats[place++] = q;
818
        }
819
      }
820

    
821
    if( place!=0 )
822
      {
823
      int[] ret = new int[place];
824
      System.arraycopy(mTmpQuats,0,ret,0,place);
825
      return ret;
826
      }
827

    
828
    return null;
829
    }
830

    
831
///////////////////////////////////////////////////////////////////////////////////////////////////
832

    
833
  public int getCubitRotationType(int cubit)
834
    {
835
    return Cubit.TYPE_NORMAL;
836
    }
837

    
838
///////////////////////////////////////////////////////////////////////////////////////////////////
839

    
840
  float[] getTrackingPoint(int cubitIndex, int cubitType)
841
    {
842
    if( cubitType!=Cubit.TYPE_NORMAL )
843
      {
844
      int variant = getCubitVariant(cubitIndex,mNumLayers);
845

    
846
      // object must have been created from JSON
847
      if( mVariantFaceIsOuter==null || mVariantFaceIsOuter[variant]==null )
848
        {
849
        mVariantFaceIsOuter = getVariantFaceIsOuter();
850
        }
851
      if( mShapes==null )
852
        {
853
        mShapes = new ObjectShape[mNumCubitVariants];
854
        }
855
      if( mShapes[variant]==null )
856
        {
857
        mShapes[variant] = getObjectShape(variant);
858
        }
859
      if( mOrigQuat==null )
860
        {
861
        mOrigQuat = new Static4D[mNumCubits];
862
        }
863
      if( mOrigQuat[cubitIndex]==null )
864
        {
865
        mOrigQuat[cubitIndex] = getCubitQuats(cubitIndex,mNumLayers);
866
        }
867

    
868
      int[][] indices = mShapes[variant].getVertIndices();
869
      int outer=-1, faces = indices.length;
870

    
871
      for(int i=0; i<faces; i++)
872
        {
873
        if( mVariantFaceIsOuter[variant][i]==1 )
874
          {
875
          outer=i;
876
          break;
877
          }
878
        }
879

    
880
      if( outer>=0 )
881
        {
882
        int vertIndex = indices[outer][0];
883
        float[] vertices = mShapes[variant].getVertices()[vertIndex];
884
        float[] ret = new float[3];
885
        float[] curpos = mOrigPos[cubitIndex];
886
        Static4D quat = mOrigQuat[cubitIndex];
887
        QuatHelper.rotateVectorByQuat(mTmp, vertices[0], vertices[1], vertices[2], 1, quat);
888

    
889
        ret[0] = mTmp[0]+computeAvg(curpos,0);
890
        ret[1] = mTmp[1]+computeAvg(curpos,1);
891
        ret[2] = mTmp[2]+computeAvg(curpos,2);
892

    
893
        return ret;
894
        }
895
      else
896
        {
897
        android.util.Log.e("D", "Error in getTrackingPoint: no outer face??");
898
        }
899
      }
900

    
901
    return null;
902
    }
903

    
904
///////////////////////////////////////////////////////////////////////////////////////////////////
905

    
906
  public int computeCurrentPuzzleFace(int type, float[] vertex)
907
    {
908
    if( type!=Cubit.TYPE_NORMAL )
909
      {
910
      Static3D[] axis = getFaceAxis();
911
      float[] dist3D = getDist3D(mNumLayers);
912
      final float MAXERR = 0.98f;
913
      int numAxis = axis.length;
914
      float x = vertex[0];
915
      float y = vertex[1];
916
      float z = vertex[2];
917

    
918
      for(int i=0; i<numAxis; i++)
919
        {
920
        Static3D ax = axis[i];
921
        float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
922
        if( len>mSize*dist3D[i]*MAXERR ) return i;
923
        }
924

    
925
      return -2;
926
      }
927

    
928
    return -1;
929
    }
930

    
931
///////////////////////////////////////////////////////////////////////////////////////////////////
932

    
933
  public float[] getCubitRowOffset(int cubitIndex)
934
    {
935
    return null;
936
    }
937

    
938
///////////////////////////////////////////////////////////////////////////////////////////////////
939

    
940
  void setRotationRowOffset(int puzzleFace, float[] offset)
941
    {
942
    mRowOffsets[puzzleFace][0] = offset[0];
943
    mRowOffsets[puzzleFace][1] = offset[1];
944
    mRowOffsets[puzzleFace][2] = offset[2];
945
    }
946

    
947
///////////////////////////////////////////////////////////////////////////////////////////////////
948

    
949
  int getNumAxis()
950
    {
951
    return mNumAxis;
952
    }
953

    
954
///////////////////////////////////////////////////////////////////////////////////////////////////
955

    
956
  public int[][] getSolvedQuats()
957
    {
958
    int[] groups = new int[mNumCubits];
959
    int numGroups = 1;
960
    int numFirst  = 0;
961

    
962
    for(int cubit=0; cubit<mNumCubits; cubit++)
963
      {
964
      groups[cubit] = retCubitSolvedStatus(cubit);
965
      if( groups[cubit]>=0 ) numGroups++;
966
      else                   numFirst++;
967
      }
968

    
969
    int firstIndex = 1;
970
    int groupIndex = 1;
971
    int[][] solvedQuats = new int[numGroups][];
972
    solvedQuats[0] = new int[1+numFirst];
973
    solvedQuats[0][0] = numFirst;
974
    Static3D[] axis = getFaceAxis();
975

    
976
    for(int cubit=0; cubit<mNumCubits; cubit++)
977
      {
978
      int group = groups[cubit];
979

    
980
      if( group<0 )
981
        {
982
        solvedQuats[0][firstIndex] = cubit;
983
        firstIndex++;
984
        }
985
      else
986
        {
987
        int[] quats = buildSolvedQuats(axis[group]);
988
        int len = quats==null ? 0 : quats.length;
989
        solvedQuats[groupIndex] = new int[2+len];
990
        solvedQuats[groupIndex][0] = 1;
991
        solvedQuats[groupIndex][1] = cubit;
992
        for(int i=0; i<len; i++) solvedQuats[groupIndex][i+2] = quats[i];
993
        groupIndex++;
994
        }
995
      }
996
/*
997
    String dbg = "SOLVED GROUPS:\n";
998

    
999
    for(int g=0; g<numGroups; g++)
1000
      {
1001
      int len = solvedQuats[g].length;
1002
      for(int i=0; i<len; i++) dbg += (" "+solvedQuats[g][i]);
1003
      dbg+="\n";
1004
      }
1005

    
1006
    android.util.Log.e("D", dbg);
1007
*/
1008
    return solvedQuats;
1009
    }
1010

    
1011
///////////////////////////////////////////////////////////////////////////////////////////////////
1012

    
1013
  public int getSolvedFunctionIndex()
1014
    {
1015
    return 0;
1016
    }
1017

    
1018
///////////////////////////////////////////////////////////////////////////////////////////////////
1019
// special SolvedQuats for the case where there are no corner of edge cubits.
1020
// first row {0} - means there are no corners or edges.
1021
// each next defines all cubits of a singe face (numCubits, firstCubit, cubit1,..,cubitN-1, quat0,..., quatM
1022

    
1023
  private boolean isSolvedCentersOnly()
1024
    {
1025
    int numGroups = mSolvedQuats.length;
1026

    
1027
    for(int group=1; group<numGroups; group++)
1028
      {
1029
      int numEntries= mSolvedQuats[group].length;
1030
      int numCubits = mSolvedQuats[group][0];
1031
      int firstCubit= mSolvedQuats[group][1];
1032
      int firstQuat = mCubits[firstCubit].mQuatIndex;
1033

    
1034
      for(int cubit=2; cubit<=numCubits; cubit++)
1035
        {
1036
        int currCubit= mSolvedQuats[group][cubit];
1037
        int currQuat = mCubits[currCubit].mQuatIndex;
1038
        boolean isGood= (firstQuat==currQuat);
1039

    
1040
        for(int q=numCubits+1; !isGood && q<numEntries; q++)
1041
          {
1042
          int quat = mSolvedQuats[group][q];
1043
          if( firstQuat == getMultQuat(currQuat,quat) ) isGood = true;
1044
          }
1045

    
1046
        if( !isGood ) return false;
1047
        }
1048
      }
1049

    
1050
    return true;
1051
    }
1052

    
1053
///////////////////////////////////////////////////////////////////////////////////////////////////
1054

    
1055
  private boolean isSolved0()
1056
    {
1057
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly();
1058

    
1059
    for( int[] solvedQuat : mSolvedQuats )
1060
      {
1061
      int numCubits = solvedQuat[0];
1062
      int firstCubit= solvedQuat[1];
1063
      int quat = mCubits[firstCubit].mQuatIndex;
1064

    
1065
      for( int cubit=2; cubit<=numCubits; cubit++ )
1066
        {
1067
        int c = solvedQuat[cubit];
1068
        if( quat != mCubits[c].mQuatIndex ) return false;
1069
        }
1070
      }
1071

    
1072
    int cubit= mSolvedQuats[0][1];
1073
    int quat0= mCubits[cubit].mQuatIndex;
1074
    int numGroups = mSolvedQuats.length;
1075

    
1076
    for(int group=1; group<numGroups; group++)
1077
      {
1078
      int firstCubit= mSolvedQuats[group][1];
1079
      int currQuat  = mCubits[firstCubit].mQuatIndex;
1080

    
1081
      if( quat0==currQuat ) continue;
1082

    
1083
      boolean isGood= false;
1084
      int numEntries= mSolvedQuats[group].length;
1085
      int numCubits = mSolvedQuats[group][0];
1086

    
1087
      for(int q=numCubits+1; q<numEntries; q++)
1088
        {
1089
        int quat = mSolvedQuats[group][q];
1090

    
1091
        if( quat0 == getMultQuat(currQuat,quat) )
1092
          {
1093
          isGood = true;
1094
          break;
1095
          }
1096
        }
1097

    
1098
      if( !isGood ) return false;
1099
      }
1100

    
1101
    return true;
1102
    }
1103

    
1104
///////////////////////////////////////////////////////////////////////////////////////////////////
1105

    
1106
  private int computeScramble(int quatNum, int centerNum)
1107
    {
1108
    float MAXDIFF = 0.01f;
1109
    float[] center= mOrigPos[centerNum];
1110
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
1111
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
1112

    
1113
    float x = result.get0();
1114
    float y = result.get1();
1115
    float z = result.get2();
1116

    
1117
    for(int c=0; c<mNumCubits; c++)
1118
      {
1119
      float[] cent = mOrigPos[c];
1120

    
1121
      float qx = cent[0] - x;
1122
      float qy = cent[1] - y;
1123
      float qz = cent[2] - z;
1124

    
1125
      if( qx>-MAXDIFF && qx<MAXDIFF &&
1126
          qy>-MAXDIFF && qy<MAXDIFF &&
1127
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
1128
      }
1129

    
1130
    return -1;
1131
    }
1132

    
1133
///////////////////////////////////////////////////////////////////////////////////////////////////
1134
// Dino4 uses this. It is solved if and only if groups of cubits
1135
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
1136
// or
1137
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
1138
// are all the same color.
1139

    
1140
  private boolean isSolved1()
1141
    {
1142
    if( mScramble==null )
1143
      {
1144
      mScramble = new int[mNumQuats][mNumCubits];
1145
      mColors   = new int[mNumCubits];
1146

    
1147
      for(int q=0; q<mNumQuats; q++)
1148
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
1149
      }
1150

    
1151
    if( mFaceMap==null )
1152
      {
1153
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
1154
      }
1155

    
1156
    for(int c=0; c<mNumCubits; c++)
1157
      {
1158
      int index = mScramble[mCubits[c].mQuatIndex][c];
1159
      mColors[index] = mFaceMap[c];
1160
      }
1161

    
1162
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
1163
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
1164
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
1165

    
1166
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
1167
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
1168
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
1169

    
1170
    return false;
1171
    }
1172

    
1173
///////////////////////////////////////////////////////////////////////////////////////////////////
1174

    
1175
  int computeRow(float[] pos, int axisIndex, int cubitType, int puzzleFace)
1176
    {
1177
    int ret=0;
1178
    int len = pos.length / 3;
1179
    Static3D axis = mAxis[axisIndex];
1180
    float axisX = axis.get0();
1181
    float axisY = axis.get1();
1182
    float axisZ = axis.get2();
1183
    float casted, xoff=0, yoff=0, zoff=0;
1184

    
1185
    if( cubitType!=Cubit.TYPE_NORMAL )
1186
      {
1187
      xoff = mRowOffsets[puzzleFace][0];
1188
      yoff = mRowOffsets[puzzleFace][1];
1189
      zoff = mRowOffsets[puzzleFace][2];
1190
      }
1191

    
1192
    for(int i=0; i<len; i++)
1193
      {
1194
      casted = (pos[3*i]+xoff)*axisX + (pos[3*i+1]+yoff)*axisY + (pos[3*i+2]+zoff)*axisZ;
1195
      ret |= computeSingleRow(axisIndex,casted);
1196
      }
1197

    
1198
    return ret;
1199
    }
1200

    
1201
///////////////////////////////////////////////////////////////////////////////////////////////////
1202

    
1203
  private int computeSingleRow(int axisIndex,float casted)
1204
    {
1205
    int num = mNumCuts[axisIndex];
1206

    
1207
    for(int i=0; i<num; i++)
1208
      {
1209
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
1210
      }
1211

    
1212
    return (1<<num);
1213
    }
1214

    
1215
///////////////////////////////////////////////////////////////////////////////////////////////////
1216

    
1217
  private boolean wasRotateApplied()
1218
    {
1219
    return mEffects.exists(mRotateEffect.getID());
1220
    }
1221

    
1222
///////////////////////////////////////////////////////////////////////////////////////////////////
1223

    
1224
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
1225
    {
1226
    return (mCubits[cubit].getRotRow(axis) & rowBitmap) != 0;
1227
    }
1228

    
1229
///////////////////////////////////////////////////////////////////////////////////////////////////
1230
// note the minus in front of the sin() - we rotate counterclockwise
1231
// when looking towards the direction where the axis increases in values.
1232

    
1233
  private Static4D makeQuaternion(float axisX, float axisY, float axisZ, int angleInDegrees)
1234
    {
1235
    while( angleInDegrees<0 ) angleInDegrees += 360;
1236
    angleInDegrees %= 360;
1237
    
1238
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
1239
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
1240

    
1241
    return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
1242
    }
1243

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

    
1246
  private synchronized void setupPosition(int[][] moves)
1247
    {
1248
    if( moves!=null )
1249
      {
1250
      Static4D quat;
1251
      int index, axisIndex, row, rowBitmap, basic, angle;
1252

    
1253
      for(int[] move: moves)
1254
        {
1255
        axisIndex= move[0];
1256
        rowBitmap= computeBitmapFromRow( move[1],axisIndex);
1257
        row      = computeRowFromBitmap( move[1] );
1258
        basic    = mBasicAngles[axisIndex][row];
1259
        angle    = move[2]*(360/basic);   // this assumes that all layers from
1260
                                          // the bitmap have the same BasicAngle.
1261
                                          // at the moment this is always true as
1262
                                          // there are no bandaged objects with
1263
                                          // different per-layer BasicAngles.
1264
        Static3D axis = mAxis[axisIndex];
1265
        float axisX = axis.get0();
1266
        float axisY = axis.get1();
1267
        float axisZ = axis.get2();
1268
        quat = makeQuaternion(axisX,axisY,axisZ,angle);
1269

    
1270
        for(int i=0; i<mNumCubits; i++)
1271
          {
1272
          mBelongs[i] = belongsToRotation(i,axisIndex,rowBitmap);
1273
          if( mBelongs[i] )
1274
            {
1275
            boolean result = mCubits[i].rotateCubit(quat);
1276
            if( !result ) debugQuat(quat,i,axisX,axisY,axisZ,angle,1);
1277
            }
1278
          }
1279

    
1280
        recomputeFaceOffsets();
1281

    
1282
        for(int i=0; i<mNumCubits; i++)
1283
          {
1284
          if( mBelongs[i] )
1285
            {
1286
            index = mCubits[i].postRotateCubit(quat);
1287
            setCubitQuat(i,mCubits[i].computeAssociation(),index);
1288
            }
1289
          else if( mCubits[i].getType()==Cubit.TYPE_FOLLOWER )
1290
            {
1291
            mCubits[i].computeRotationRow();
1292
            setCubitQuat(i,mCubits[i].computeAssociation(),mCubits[i].mQuatIndex);
1293
            }
1294
          }
1295
        }
1296
      }
1297
    }
1298

    
1299
///////////////////////////////////////////////////////////////////////////////////////////////////
1300

    
1301
  public int getScrambleType()
1302
    {
1303
    return 0;
1304
    }
1305

    
1306
///////////////////////////////////////////////////////////////////////////////////////////////////
1307

    
1308
  int computeBitmapFromRow(int rowBitmap, int axis)
1309
    {
1310
    if( mIsBandaged )
1311
      {
1312
      int bitmap, initBitmap=0;
1313

    
1314
      while( initBitmap!=rowBitmap )
1315
        {
1316
        initBitmap = rowBitmap;
1317

    
1318
        for(int cubit=0; cubit<mNumCubits; cubit++)
1319
          {
1320
          bitmap = mCubits[cubit].getRotRow(axis);
1321
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
1322
          }
1323
        }
1324
      }
1325

    
1326
    return rowBitmap;
1327
    }
1328

    
1329
///////////////////////////////////////////////////////////////////////////////////////////////////
1330

    
1331
  private int computeRowFromBitmap(int rowBitmap)
1332
    {
1333
    int index = 0;
1334

    
1335
    while(index<32)
1336
      {
1337
      if( (rowBitmap&0x1) != 0 ) return index;
1338
      rowBitmap>>=1;
1339
      index++;
1340
      }
1341
    return 0;
1342
    }
1343

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

    
1348
  void clampPos(float[] pos, int offset)
1349
    {
1350
    float currError, minError = Float.MAX_VALUE;
1351
    int minErrorIndex1 = -1;
1352
    int minErrorIndex2 = -1;
1353

    
1354
    float x = pos[offset  ];
1355
    float y = pos[offset+1];
1356
    float z = pos[offset+2];
1357

    
1358
    float xo,yo,zo;
1359

    
1360
    for(int i=0; i<mNumCubits; i++)
1361
      {
1362
      int len = mOrigPos[i].length / 3;
1363

    
1364
      for(int j=0; j<len; j++)
1365
        {
1366
        xo = mOrigPos[i][3*j  ];
1367
        yo = mOrigPos[i][3*j+1];
1368
        zo = mOrigPos[i][3*j+2];
1369

    
1370
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
1371

    
1372
        if( currError<minError )
1373
          {
1374
          minError = currError;
1375
          minErrorIndex1 = i;
1376
          minErrorIndex2 = j;
1377
          }
1378
        }
1379
      }
1380

    
1381
    if( minError< 0.05f ) // TODO: 0.05 ?
1382
      {
1383
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
1384
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
1385
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
1386
      }
1387
    }
1388

    
1389
///////////////////////////////////////////////////////////////////////////////////////////////////
1390
// remember about the double cover or unit quaternions!
1391

    
1392
  int mulQuat(int q1, int q2)
1393
    {
1394
    Static4D result = QuatHelper.quatMultiply(mObjectQuats[q1],mObjectQuats[q2]);
1395

    
1396
    float rX = result.get0();
1397
    float rY = result.get1();
1398
    float rZ = result.get2();
1399
    float rW = result.get3();
1400

    
1401
    final float MAX_ERROR = 0.1f;
1402
    float dX,dY,dZ,dW;
1403

    
1404
    for(int i=0; i<mNumQuats; i++)
1405
      {
1406
      dX = mObjectQuats[i].get0() - rX;
1407
      dY = mObjectQuats[i].get1() - rY;
1408
      dZ = mObjectQuats[i].get2() - rZ;
1409
      dW = mObjectQuats[i].get3() - rW;
1410

    
1411
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
1412
          dY<MAX_ERROR && dY>-MAX_ERROR &&
1413
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
1414
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
1415

    
1416
      dX = mObjectQuats[i].get0() + rX;
1417
      dY = mObjectQuats[i].get1() + rY;
1418
      dZ = mObjectQuats[i].get2() + rZ;
1419
      dW = mObjectQuats[i].get3() + rW;
1420

    
1421
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
1422
          dY<MAX_ERROR && dY>-MAX_ERROR &&
1423
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
1424
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
1425
      }
1426

    
1427
    return -1;
1428
    }
1429

    
1430
///////////////////////////////////////////////////////////////////////////////////////////////////
1431

    
1432
  private float getAngle()
1433
    {
1434
    int pointNum = mRotationAngle.getNumPoints();
1435

    
1436
    if( pointNum>=1 )
1437
      {
1438
      return mRotationAngle.getPoint(pointNum-1).get0();
1439
      }
1440
    else
1441
      {
1442
      mInterface.reportProblem("points in RotationAngle: "+pointNum, false);
1443
      return 0;
1444
      }
1445
    }
1446

    
1447
///////////////////////////////////////////////////////////////////////////////////////////////////
1448

    
1449
  void setLibInterface(ObjectLibInterface inter)
1450
    {
1451
    mInterface = inter;
1452
    }
1453

    
1454
///////////////////////////////////////////////////////////////////////////////////////////////////
1455

    
1456
  void applyScrambles(int[][] moves)
1457
    {
1458
    setupPosition(moves);
1459
    }
1460

    
1461
///////////////////////////////////////////////////////////////////////////////////////////////////
1462

    
1463
  void initializeObject(int[][] moves)
1464
    {
1465
    solve();
1466
    setupPosition(moves);
1467
    }
1468

    
1469
///////////////////////////////////////////////////////////////////////////////////////////////////
1470

    
1471
  synchronized void removeRotationNow()
1472
    {
1473
    float angle = getAngle();
1474
    double nearestAngleInRadians = angle*Math.PI/180;
1475
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
1476
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
1477
    float axisX = mAxis[mCurrentRotAxis].get0();
1478
    float axisY = mAxis[mCurrentRotAxis].get1();
1479
    float axisZ = mAxis[mCurrentRotAxis].get2();
1480
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
1481

    
1482
    mRotationAngle.removeAll();
1483
    mRotationAngleStatic.set0(0);
1484

    
1485
    for(int i=0; i<mNumCubits; i++)
1486
      {
1487
      mBelongs[i] = belongsToRotation(i, mCurrentRotAxis,mRotRowBitmap);
1488
      if( mBelongs[i] )
1489
        {
1490
        boolean result = mCubits[i].rotateCubit(quat);
1491
        if( !result ) debugQuat(quat,i,axisX,axisY,axisZ,angle,2);
1492
        }
1493
      }
1494

    
1495
    recomputeFaceOffsets();
1496

    
1497
    for(int i=0; i<mNumCubits; i++)
1498
      {
1499
      if( mBelongs[i] )
1500
        {
1501
        int index = mCubits[i].postRotateCubit(quat);
1502
        setCubitQuat(i,mCubits[i].computeAssociation(),index);
1503
        }
1504
      else if( mCubits[i].getType()==Cubit.TYPE_FOLLOWER )
1505
        {
1506
        mCubits[i].computeRotationRow();
1507
        setCubitQuat(i,mCubits[i].computeAssociation(),mCubits[i].mQuatIndex);
1508
        }
1509
      }
1510
    }
1511

    
1512
///////////////////////////////////////////////////////////////////////////////////////////////////
1513

    
1514
  private void recomputeFaceOffsets()
1515
    {
1516
    for(int i=0; i<mNumPuzzleFaces; i++)
1517
      {
1518
      mRowOffsets[i][0] =0;
1519
      mRowOffsets[i][1] =0;
1520
      mRowOffsets[i][2] =0;
1521
      }
1522

    
1523
    for(int i=0; i<mNumCubits; i++)
1524
      if( mCubits[i].getType()==Cubit.TYPE_DECIDER )
1525
        {
1526
        float[] offset = mCubits[i].getOffset();
1527
        int face = mCubits[i].getPuzzleFace();
1528
        mRowOffsets[face][0] = offset[0];
1529
        mRowOffsets[face][1] = offset[1];
1530
        mRowOffsets[face][2] = offset[2];
1531
        }
1532
    }
1533

    
1534
///////////////////////////////////////////////////////////////////////////////////////////////////
1535

    
1536
  long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
1537
    {
1538
    if( wasRotateApplied() )
1539
      {
1540
      float angle = getAngle();
1541
      mRotationAngleStatic.set0(angle);
1542
      mRotationAngleFinal.set0(nearestAngleInDegrees);
1543
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
1544

    
1545
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
1546
      mRotationAngle.resetToBeginning();
1547
      mRotationAngle.removeAll();
1548
      mRotationAngle.add(mRotationAngleStatic);
1549
      mRotationAngle.add(mRotationAngleMiddle);
1550
      mRotationAngle.add(mRotationAngleFinal);
1551
      mRotateEffect.notifyWhenFinished(listener);
1552

    
1553
      return mRotateEffect.getID();
1554
      }
1555

    
1556
    return 0;
1557
    }
1558

    
1559
///////////////////////////////////////////////////////////////////////////////////////////////////
1560

    
1561
  synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
1562
    {
1563
    if( wasRotateApplied() )
1564
      {
1565
      mCurrentRotAxis = axis;
1566
      mRotRowBitmap= computeBitmapFromRow( rowBitmap,axis );
1567

    
1568
      mRotationAngleStatic.set0(0.0f);
1569
      mRotationAxis.set( mAxis[axis] );
1570
      mRotationAngle.setDuration(durationMillis);
1571
      mRotationAngle.resetToBeginning();
1572
      mRotationAngle.add(new Static1D(0));
1573
      mRotationAngle.add(new Static1D(angle));
1574
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*mMaxNumLayers) , -1);
1575
      mRotateEffect.notifyWhenFinished(listener);
1576

    
1577
      return mRotateEffect.getID();
1578
      }
1579

    
1580
    return 0;
1581
    }
1582

    
1583
///////////////////////////////////////////////////////////////////////////////////////////////////
1584

    
1585
  void continueRotation(float angleInDegrees)
1586
    {
1587
    mRotationAngleStatic.set0(angleInDegrees);
1588
    }
1589

    
1590
///////////////////////////////////////////////////////////////////////////////////////////////////
1591

    
1592
  synchronized void beginNewRotation(int axis, int row )
1593
    {
1594
    if( axis<0 || axis>=mNumAxis )
1595
      {
1596
      android.util.Log.e("object", "invalid rotation axis: "+axis);
1597
      return;
1598
      }
1599
    if( row<0 || row>=mNumLayers[axis] )
1600
      {
1601
      android.util.Log.e("object", "invalid rotation row: "+row);
1602
      return;
1603
      }
1604

    
1605
    mCurrentRotAxis = axis;
1606
    mRotRowBitmap= computeBitmapFromRow( (1<<row),axis );
1607
    mRotationAngleStatic.set0(0.0f);
1608
    mRotationAxis.set( mAxis[axis] );
1609
    mRotationAngle.add(mRotationAngleStatic);
1610
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*mMaxNumLayers) , -1);
1611
    }
1612

    
1613
///////////////////////////////////////////////////////////////////////////////////////////////////
1614

    
1615
  void setTextureMap(int cubit, int face, int color)
1616
    {
1617
    int variant  = getCubitVariant(cubit,mNumLayers);
1618
    int shape    = getVariantStickerShape(variant,face);
1619
    int texIndex = color<0 || shape<0 ? mNumTextures-mNumOverrides : shape*mNumFaceColors + color;
1620
    int row      = (mNumTexRows-1) - texIndex/mNumTexCols;
1621
    int col      = texIndex%mNumTexCols;
1622

    
1623
    final float ratioW = 1.0f/mNumTexCols;
1624
    final float ratioH = 1.0f/mNumTexRows;
1625
    final Static4D[] maps = new Static4D[1];
1626
    maps[0] = new Static4D(col*ratioW, row*ratioH, ratioW, ratioH);
1627
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit+face);
1628
    }
1629

    
1630
///////////////////////////////////////////////////////////////////////////////////////////////////
1631

    
1632
  private int getCubitFaceColor(int cubit, int face)
1633
    {
1634
    int puzzleFace = getCubitFaceMap(cubit,face);
1635
    if( puzzleFace>=0 ) puzzleFace %= mNumFaceColors;
1636
    return puzzleFace;
1637
    }
1638

    
1639
///////////////////////////////////////////////////////////////////////////////////////////////////
1640

    
1641
  public int getCubitFaceMap(int cubit, int face)
1642
    {
1643
    int numFaces = mCubitFaceColors[cubit].length;
1644
    int puzzleFace = face<numFaces ? mCubitFaceColors[cubit][face] : -1;
1645
    return puzzleFace<0 ? -1 : puzzleFace;
1646
    }
1647

    
1648
///////////////////////////////////////////////////////////////////////////////////////////////////
1649

    
1650
  void resetAllTextureMaps()
1651
    {
1652
    final float ratioW = 1.0f/mNumTexCols;
1653
    final float ratioH = 1.0f/mNumTexRows;
1654
    int cubColor, stiShape, texIndex, variant, row, col;
1655

    
1656
    for(int cubit=0; cubit<mNumCubits; cubit++)
1657
      {
1658
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1659
      variant = getCubitVariant(cubit,mNumLayers);
1660

    
1661
      for(int face=0; face<mNumCubitFaces; face++)
1662
        {
1663
        cubColor = getCubitFaceColor(cubit,face);
1664
        stiShape = getVariantStickerShape(variant,face);
1665
        texIndex = cubColor<0 || stiShape<0 ? mNumTextures-mNumOverrides : stiShape*mNumFaceColors + cubColor;
1666
        row      = (mNumTexRows-1) - texIndex/mNumTexCols;
1667
        col      = texIndex%mNumTexCols;
1668

    
1669
        maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1670
        }
1671

    
1672
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1673
      }
1674

    
1675
    overrideCubitFaceColor();
1676
    }
1677

    
1678
///////////////////////////////////////////////////////////////////////////////////////////////////
1679

    
1680
  private void overrideCubitFaceColor()
1681
    {
1682
    final float ratioW = 1.0f/mNumTexCols;
1683
    final float ratioH = 1.0f/mNumTexRows;
1684

    
1685
    for(int i=0; i<mNumOverrides; i++)
1686
      {
1687
      int[] cubitFaces = mStickerOverrides[i].getCubitFaces();
1688
      int length = cubitFaces.length/2;
1689

    
1690
      for(int j=0; j<length; j++)
1691
        {
1692
        final Static4D[] maps = new Static4D[1];
1693
        int color = mNumTextures-mNumOverrides+1+i;
1694
        int row   = (mNumTexRows-1) - color/mNumTexCols;
1695
        int col   = color%mNumTexCols;
1696
        int cubit = cubitFaces[2*j];
1697
        int face  = cubitFaces[2*j+1];
1698
        maps[0] = new Static4D(col*ratioW, row*ratioH, ratioW, ratioH);
1699
        mMesh.setTextureMap(maps,mNumCubitFaces*cubit+face);
1700
        }
1701
      }
1702
    }
1703

    
1704
///////////////////////////////////////////////////////////////////////////////////////////////////
1705

    
1706
  void releaseResources()
1707
    {
1708
    mTexture.markForDeletion();
1709
    mMesh.markForDeletion();
1710
    mEffects.markForDeletion();
1711

    
1712
    for(int j=0; j<mNumCubits; j++)
1713
      {
1714
      mCubits[j].releaseResources();
1715
      }
1716
    }
1717

    
1718
///////////////////////////////////////////////////////////////////////////////////////////////////
1719

    
1720
  private void setCubitQuat(int cubit, int andAssociation, int equAssociation)
1721
    {
1722
    if( !mIsInMixupMode )
1723
      {
1724
      mMesh.setEffectAssociation(cubit,andAssociation,equAssociation);
1725
      }
1726
    else
1727
      {
1728
      mMesh.setEffectAssociation(cubit,andAssociation,cubit);
1729
      Static4D tmp = mObjectQuats[equAssociation];
1730
      mMixupModeQuats[cubit].set(tmp);
1731
      }
1732
    }
1733

    
1734
///////////////////////////////////////////////////////////////////////////////////////////////////
1735

    
1736
  synchronized void restorePreferences(SharedPreferences preferences)
1737
    {
1738
    boolean error = false;
1739
    String key = getShortName();
1740

    
1741
    for(int i=0; i<mNumCubits; i++)
1742
      {
1743
      mQuatDebug[i] = mCubits[i].restorePreferences(key,preferences);
1744

    
1745
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1746
        {
1747
        boolean result = mCubits[i].rotateCubit(mObjectQuats[mQuatDebug[i]]);
1748
        if( !result ) debugQuat(mObjectQuats[mQuatDebug[i]],i,0,0,0,mQuatDebug[i],3);
1749
        }
1750
      else { error = true; break; }
1751
      }
1752

    
1753
    if( !error )
1754
      {
1755
      recomputeFaceOffsets();
1756

    
1757
      for(int i=0; i<mNumCubits; i++)
1758
        {
1759
        if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1760
          {
1761
          mCubits[i].computeRotationRow();
1762
          setCubitQuat(i,mCubits[i].computeAssociation(),mQuatDebug[i]);
1763
          }
1764
        else { error = true; break; }
1765
        }
1766
      }
1767

    
1768
    if( error )
1769
      {
1770
      for(int i=0; i<mNumCubits; i++)
1771
        {
1772
        mCubits[i].solve();
1773
        setCubitQuat(i,mCubits[i].computeAssociation(),0);
1774
        }
1775
      }
1776
    }
1777

    
1778
///////////////////////////////////////////////////////////////////////////////////////////////////
1779

    
1780
  void savePreferences(SharedPreferences.Editor editor)
1781
    {
1782
    String key = getShortName();
1783
    for(int i=0; i<mNumCubits; i++) mCubits[i].savePreferences(key,editor);
1784
    }
1785

    
1786
///////////////////////////////////////////////////////////////////////////////////////////////////
1787

    
1788
  public void removePreferences(SharedPreferences.Editor editor)
1789
    {
1790
    String key = getShortName();
1791
    for(int i=0; i<mNumCubits; i++) mCubits[i].removePreferences(key,editor);
1792
    }
1793

    
1794
///////////////////////////////////////////////////////////////////////////////////////////////////
1795

    
1796
  private float computeRadiusCorrection(float[] sticker, int curr, int len)
1797
    {
1798
    final float A = 0.8f;  // 0<A<1
1799

    
1800
    int prev = curr>0 ? curr-1 : len-1;
1801
    int next = curr<len-1 ? curr+1 : 0;
1802

    
1803
    float v1x = sticker[2*prev  ]-sticker[2*curr  ];
1804
    float v1y = sticker[2*prev+1]-sticker[2*curr+1];
1805
    float v2x = sticker[2*next  ]-sticker[2*curr  ];
1806
    float v2y = sticker[2*next+1]-sticker[2*curr+1];
1807

    
1808
    float len1= v1x*v1x+v1y*v1y;
1809
    float len2= v2x*v2x+v2y*v2y;
1810

    
1811
    float cos = (v1x*v2x+v1y*v2y) / ( (float)Math.sqrt(len1*len2) );
1812

    
1813
    return 1-A*cos;
1814
    }
1815

    
1816
///////////////////////////////////////////////////////////////////////////////////////////////////
1817
// Radius of the sphere circumscribed on the puzzle. Needed for pillowing.
1818
//
1819
// This won't work correctly for pillowing off-center puzzles (e.g. mirrors) - for those we'd need
1820
// to introduce the concept of a 'sink center' as well.
1821
//
1822
// public because needed in TouchControlShapemod
1823

    
1824
  public float getCircumscribedRadius()
1825
    {
1826
    switch(mNumPuzzleFaces)
1827
      {
1828
      case  4: return (SQ6/4)*mSize;
1829
      case  6: return (SQ3/2)*mSize;
1830
      case  8: return (SQ2/2)*mSize;
1831
      case 12: return (SQ3/2)*((SQ5+1)/2)*mSize;
1832
      case 16: return 0.50f*mSize;
1833
      }
1834

    
1835
    return 0.0f;
1836
    }
1837

    
1838
///////////////////////////////////////////////////////////////////////////////////////////////////
1839

    
1840
  public ObjectSticker retSticker(int sticker)
1841
    {
1842
    if( mStickers==null )
1843
      {
1844
      float rad = getStickerRadius();
1845
      float str = getStickerStroke();
1846
      float[][] angles = getStickerAngles();
1847
      int numStickers = mStickerCoords.length;
1848
      mStickers = new ObjectSticker[numStickers];
1849

    
1850
      for(int s=0; s<numStickers; s++)
1851
        {
1852
        float scale = mStickerScales.length>s ? mStickerScales[s] : 1.0f;
1853
        float radius = rad / scale;
1854
        float stroke = str / scale;
1855
        int len = mStickerCoords[s].length/2;
1856
        float[] radii = new float[len];
1857
        for(int r=0; r<len; r++) radii[r] = radius*computeRadiusCorrection(mStickerCoords[s],r,len);
1858
        mStickers[s] = new ObjectSticker(mStickerCoords[s],angles==null ? null : angles[s],radii,stroke);
1859
        }
1860
      }
1861

    
1862
    return mStickers[sticker];
1863
    }
1864

    
1865
///////////////////////////////////////////////////////////////////////////////////////////////////
1866
// some objects (currently Kilominx,Ivy,Rex) might want to change the stickers.
1867

    
1868
  public void adjustStickerCoords()
1869
    {
1870

    
1871
    }
1872

    
1873
///////////////////////////////////////////////////////////////////////////////////////////////////
1874

    
1875
  public Static4D[] getQuats()
1876
    {
1877
    if( mObjectQuats==null )
1878
      {
1879
      mObjectQuats = QuatGroupGenerator.computeGroup(mAxis,mBasicAngles);
1880
      }
1881

    
1882
    return mObjectQuats;
1883
    }
1884

    
1885
///////////////////////////////////////////////////////////////////////////////////////////////////
1886

    
1887
  public int[][] getVariantFaceIsOuter()
1888
    {
1889
    return mVariantFaceIsOuter;
1890
    }
1891

    
1892
///////////////////////////////////////////////////////////////////////////////////////////////////
1893

    
1894
  public int getInternalColor()
1895
    {
1896
    return COLOR_INTERNAL;
1897
    }
1898

    
1899
///////////////////////////////////////////////////////////////////////////////////////////////////
1900
// the getFaceColors + final INTERNAL_COLOR in a grid (so that we do not exceed the maximum texture size)
1901

    
1902
  private void createTexture()
1903
    {
1904
    Paint paint = new Paint();
1905
    mBitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_4444);
1906
    Canvas canvas = new Canvas(mBitmap);
1907

    
1908
    paint.setAntiAlias(true);
1909
    paint.setTextAlign(Paint.Align.CENTER);
1910
    paint.setStyle(Paint.Style.FILL);
1911
    paint.setColor(getInternalColor());
1912
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
1913

    
1914
    int texture = 0;
1915
    FactorySticker factory = FactorySticker.getInstance();
1916

    
1917
    for(int row=0; row<mNumTexRows; row++)
1918
      for(int col=0; col<mNumTexCols; col++)
1919
        {
1920
        if( texture<mNumTextures-mNumOverrides )
1921
          {
1922
          ObjectSticker sticker = retSticker(texture/mNumFaceColors);
1923
          int color = getColor(texture%mNumFaceColors);
1924
          factory.drawRoundedPolygon(canvas, paint, col*TEXTURE_HEIGHT, (mNumTexRows-row)*TEXTURE_HEIGHT, color, sticker);
1925
          }
1926
        else if( texture>mNumTextures-mNumOverrides && texture<=mNumTextures )
1927
          {
1928
          int color = mStickerOverrides[mNumTextures-texture].getColor();
1929
          factory.drawSolidColor(canvas, paint, col*TEXTURE_HEIGHT, (mNumTexRows-row)*TEXTURE_HEIGHT, color);
1930
          }
1931

    
1932
        texture++;
1933
        }
1934
    }
1935

    
1936
///////////////////////////////////////////////////////////////////////////////////////////////////
1937

    
1938
  void setTexture()
1939
    {
1940
    if( mBitmap==null ) createTexture();
1941

    
1942
    if( !mTexture.setTextureAlreadyInverted(mBitmap) )
1943
      {
1944
      int max = DistortedLibrary.getMaxTextureSize();
1945
      mInterface.reportProblem("failed to set texture of size "+mBitmap.getWidth()+"x"+mBitmap.getHeight()+" max is "+max, true);
1946
      }
1947
    }
1948

    
1949
///////////////////////////////////////////////////////////////////////////////////////////////////
1950

    
1951
  void setObjectRatioNow(float sc, int nodeSize)
1952
    {
1953
    mObjectScreenRatio = sc;
1954
    float scale = mObjectScreenRatio*mInitScreenRatio*nodeSize/mSize;
1955
    mObjectScale.set(scale,scale,scale);
1956

    
1957
    if( mTouchControl ==null ) mTouchControl = getTouchControl();
1958
    mTouchControl.setObjectRatio(mObjectScreenRatio*mInitScreenRatio);
1959
    }
1960

    
1961
///////////////////////////////////////////////////////////////////////////////////////////////////
1962

    
1963
  void setObjectRatio(float sizeChange, int nodeSize)
1964
    {
1965
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1966

    
1967
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1968
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1969

    
1970
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1971
    }
1972

    
1973
///////////////////////////////////////////////////////////////////////////////////////////////////
1974

    
1975
  void setNodeSize(int nodeSize)
1976
    {
1977
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1978
    }
1979

    
1980
///////////////////////////////////////////////////////////////////////////////////////////////////
1981

    
1982
  public float getRatio()
1983
    {
1984
    return mObjectScreenRatio;
1985
    }
1986

    
1987
///////////////////////////////////////////////////////////////////////////////////////////////////
1988

    
1989
  public float getObjectRatio()
1990
    {
1991
    return mObjectScreenRatio*mInitScreenRatio;
1992
    }
1993

    
1994
///////////////////////////////////////////////////////////////////////////////////////////////////
1995

    
1996
  boolean isSolved()
1997
    {
1998
    if( mSolvedFunctionIndex==0 ) return isSolved0();
1999
    if( mSolvedFunctionIndex==1 ) return isSolved1();
2000

    
2001
    return false;
2002
    }
2003

    
2004
///////////////////////////////////////////////////////////////////////////////////////////////////
2005

    
2006
  int computeNearestAngle(int basicAngle, float angle, float speed)
2007
    {
2008
    int nearestAngle = 360/basicAngle;
2009
    int tmp = (int)((angle+nearestAngle/2)/nearestAngle);
2010
    if( angle< -(nearestAngle*0.5) ) tmp-=1;
2011

    
2012
    if( tmp!=0 ) return nearestAngle*tmp;
2013

    
2014
    return speed> 1.2f ? nearestAngle*(angle>0 ? 1:-1) : 0;
2015
    }
2016

    
2017
///////////////////////////////////////////////////////////////////////////////////////////////////
2018
// INTERNAL API - those are called from 'effects' package
2019
///////////////////////////////////////////////////////////////////////////////////////////////////
2020

    
2021
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
2022
    {
2023
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total, getSignature() );
2024
    }
2025

    
2026
///////////////////////////////////////////////////////////////////////////////////////////////////
2027

    
2028
  public Static4D getRotationQuat()
2029
    {
2030
    return mQuat;
2031
    }
2032

    
2033
///////////////////////////////////////////////////////////////////////////////////////////////////
2034

    
2035
  public float getSize()
2036
    {
2037
    return mSize;
2038
    }
2039

    
2040
///////////////////////////////////////////////////////////////////////////////////////////////////
2041

    
2042
  public void applyEffect(Effect effect, int position)
2043
    {
2044
    mEffects.apply(effect, position);
2045
    }
2046

    
2047
///////////////////////////////////////////////////////////////////////////////////////////////////
2048

    
2049
  public void removeEffect(long effectID)
2050
    {
2051
    mEffects.abortById(effectID);
2052
    }
2053

    
2054
///////////////////////////////////////////////////////////////////////////////////////////////////
2055

    
2056
  public MeshBase getObjectMesh()
2057
    {
2058
    return mMesh;
2059
    }
2060

    
2061
///////////////////////////////////////////////////////////////////////////////////////////////////
2062

    
2063
  public DistortedEffects getObjectEffects()
2064
    {
2065
    return mEffects;
2066
    }
2067

    
2068
///////////////////////////////////////////////////////////////////////////////////////////////////
2069

    
2070
  public int getCubitType(int cubit)
2071
    {
2072
    return mCubits[cubit].getType();
2073
    }
2074

    
2075
///////////////////////////////////////////////////////////////////////////////////////////////////
2076

    
2077
  public float[] getCubitOffset(int cubit)
2078
    {
2079
    return mCubits[cubit].getOffset();
2080
    }
2081

    
2082
///////////////////////////////////////////////////////////////////////////////////////////////////
2083

    
2084
  public ObjectStickerOverride[] getStickerOverrides()
2085
    {
2086
    return null;
2087
    }
2088

    
2089
///////////////////////////////////////////////////////////////////////////////////////////////////
2090

    
2091
  public boolean getError()
2092
    {
2093
    return mError;
2094
    }
2095

    
2096
///////////////////////////////////////////////////////////////////////////////////////////////////
2097

    
2098
  public String getErrorString()
2099
    {
2100
    return mErrorString;
2101
    }
2102

    
2103
///////////////////////////////////////////////////////////////////////////////////////////////////
2104
// PUBLIC API
2105
///////////////////////////////////////////////////////////////////////////////////////////////////
2106

    
2107
  public int getCubitFaceColorIndex(int cubit, int face)
2108
    {
2109
    Static4D texMap = mMesh.getTextureMap(mNumFaceColors *cubit + face);
2110

    
2111
    int x = (int)(texMap.get0()/texMap.get2());
2112
    int y = (int)(texMap.get1()/texMap.get3());
2113

    
2114
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
2115
    }
2116

    
2117
///////////////////////////////////////////////////////////////////////////////////////////////////
2118

    
2119
  public int[] getNumLayers()
2120
    {
2121
    return mNumLayers;
2122
    }
2123

    
2124
///////////////////////////////////////////////////////////////////////////////////////////////////
2125

    
2126
  public synchronized void solve()
2127
    {
2128
    for(int i=0; i<mNumCubits; i++)
2129
      {
2130
      mCubits[i].solve();
2131
      }
2132

    
2133
    recomputeFaceOffsets();
2134

    
2135
    for(int i=0; i<mNumCubits; i++)
2136
      {
2137
      mCubits[i].computeRotationRow();
2138
      setCubitQuat(i,mCubits[i].computeAssociation(),0);
2139
      }
2140
    }
2141

    
2142
///////////////////////////////////////////////////////////////////////////////////////////////////
2143

    
2144
  public int getCubitQuatIndex(int cubit)
2145
    {
2146
    return (cubit>=0 && cubit<mNumCubits) ? mCubits[cubit].mQuatIndex : 0;
2147
    }
2148

    
2149
///////////////////////////////////////////////////////////////////////////////////////////////////
2150

    
2151
  public int getCubitRotRow(int cubit, int axis)
2152
    {
2153
    return mCubits[cubit].getRotRow(axis);
2154
    }
2155

    
2156
///////////////////////////////////////////////////////////////////////////////////////////////////
2157

    
2158
  public Bitmap getStickerBitmap()
2159
    {
2160
    return mBitmap;
2161
    }
2162

    
2163
///////////////////////////////////////////////////////////////////////////////////////////////////
2164

    
2165
  public DistortedNode getNode()
2166
    {
2167
    return mNode;
2168
    }
2169

    
2170
///////////////////////////////////////////////////////////////////////////////////////////////////
2171

    
2172
  public int getNumStickerTypes()
2173
    {
2174
    return mNumStickerTypes;
2175
    }
2176

    
2177
///////////////////////////////////////////////////////////////////////////////////////////////////
2178

    
2179
  public String reportState()
2180
    {
2181
    StringBuilder builder = new StringBuilder();
2182

    
2183
    for(int i=0; i<mNumCubits; i++ )
2184
      {
2185
      if( i>0 ) builder.append('.');
2186
      builder.append(mCubits[i].mQuatIndex);
2187
      }
2188

    
2189
    return builder.toString();
2190
    }
2191

    
2192
///////////////////////////////////////////////////////////////////////////////////////////////////
2193
// this is here only so it can be overridden in TwistyJSON so that we can get this from JSON.
2194

    
2195
  public int getNumCubitFaces()
2196
    {
2197
    return 0;
2198
    }
2199

    
2200
///////////////////////////////////////////////////////////////////////////////////////////////////
2201
// 1.0 - i.e. no pillowing - by default.
2202
// The coeff is really param of the 'sink' vertex effect - if it is not equal to 1.0, we apply the
2203
// sink effect [centered at (0,0,0)] to the whole mesh as the last step of composing it.
2204

    
2205
  public float getPillowCoeff()
2206
    {
2207
    return 1.0f;
2208
    }
2209

    
2210
///////////////////////////////////////////////////////////////////////////////////////////////////
2211

    
2212
  public TouchControl getTouchControl()
2213
    {
2214
    if( mTouchControl==null )
2215
      {
2216
      switch(getTouchControlType())
2217
        {
2218
        case TC_TETRAHEDRON      : mTouchControl = new TouchControlTetrahedron(this);
2219
                                   break;
2220
        case TC_HEXAHEDRON       : mTouchControl = new TouchControlHexahedron(this);
2221
                                   break;
2222
        case TC_OCTAHEDRON       : mTouchControl = new TouchControlOctahedron(this);
2223
                                   break;
2224
        case TC_DODECAHEDRON     : mTouchControl = new TouchControlDodecahedron(this);
2225
                                   break;
2226
        case TC_ICOSAHEDRON      : mTouchControl = new TouchControlIcosahedron(this);
2227
                                   break;
2228
        case TC_CUBOID           : int[] numLayers = getNumLayers();
2229
                                   mTouchControl = new TouchControlCuboids(this,getDist3D(numLayers));
2230
                                   break;
2231
        case TC_BALL             : mTouchControl = new TouchControlBall(this);
2232
                                   break;
2233
        case TC_CHANGING_MIRROR  : mTouchControl = new TouchControlMirror(this);
2234
                                   break;
2235
        case TC_CHANGING_SQUARE  : mTouchControl = new TouchControlSquare(this);
2236
                                   break;
2237
        case TC_CHANGING_SHAPEMOD: mTouchControl = new TouchControlShapemod(this);
2238
                                   break;
2239
        }
2240
      }
2241
    return mTouchControl;
2242
    }
2243

    
2244
///////////////////////////////////////////////////////////////////////////////////////////////////
2245

    
2246
  protected void setReader(JsonReader reader)
2247
    {
2248
    // empty
2249
    }
2250

    
2251
///////////////////////////////////////////////////////////////////////////////////////////////////
2252
  // for JSON only
2253
  public abstract int getTouchControlType();
2254
  public abstract int getTouchControlSplit();
2255
  public abstract boolean[][] getLayerRotatable(int[] numLayers);
2256
  public abstract int[][][] getEnabled();
2257
  public abstract float[] getDist3D(int[] numLayers);
2258
  public abstract Static3D[] getFaceAxis();
2259
  public abstract int[][] getScrambleEdges();
2260
  public abstract float[][] getCuts(int[] numLayers);
2261
  public abstract float getStickerRadius();
2262
  public abstract float getStickerStroke();
2263
  public abstract float[][] getStickerAngles();
2264
  public abstract int getCubitVariant(int cubit, int[] numLayers);
2265
  public abstract ObjectShape getObjectShape(int variant);
2266
  public abstract ObjectFaceShape getObjectFaceShape(int variant);
2267
  public abstract ObjectVertexEffects getVertexEffects(int variant);
2268
  public abstract int getNumCubitVariants(int[] numLayers);
2269
  public abstract float[][] getCubitPositions(int[] numLayers);
2270
  public abstract Static4D getCubitQuats(int cubit, int[] numLayers);
2271
  public abstract int getNumFaceColors();
2272
  public abstract float getScreenRatio();
2273
  public abstract int getColor(int face);
2274
  public abstract String getShortName();
2275
  public abstract ObjectSignature getSignature();
2276

    
2277
  // not only for JSON
2278
  public abstract Static3D[] getRotationAxis();
2279
  public abstract int[][] getBasicAngles();
2280
  public abstract int getNumFaces();
2281
  public abstract String getObjectName();
2282
  public abstract String getInventor();
2283
  public abstract int getYearOfInvention();
2284
  public abstract int getComplexity();
2285
  public abstract int getFOV();
2286
  public abstract String[][] getTutorials();
2287
  }
(8-8/9)