Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObject.java @ 7d2fe403

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.objectlib.main;
21

    
22
import java.io.DataInputStream;
23
import java.io.IOException;
24
import java.io.InputStream;
25
import java.util.Random;
26

    
27
import android.content.SharedPreferences;
28
import android.graphics.Bitmap;
29
import android.graphics.Canvas;
30
import android.graphics.Paint;
31

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

    
52
import org.distorted.objectlib.helpers.FactoryCubit;
53
import org.distorted.objectlib.helpers.FactorySticker;
54
import org.distorted.objectlib.helpers.ObjectFaceShape;
55
import org.distorted.objectlib.helpers.ObjectLibInterface;
56
import org.distorted.objectlib.helpers.ObjectShape;
57
import org.distorted.objectlib.helpers.ObjectSignature;
58
import org.distorted.objectlib.helpers.ObjectSticker;
59
import org.distorted.objectlib.helpers.QuatGroupGenerator;
60
import org.distorted.objectlib.scrambling.ScrambleState;
61
import org.distorted.objectlib.scrambling.ObjectScrambler;
62
import org.distorted.objectlib.json.JsonReader;
63
import org.distorted.objectlib.touchcontrol.*;
64

    
65
import static org.distorted.objectlib.touchcontrol.TouchControl.*;
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
public abstract class TwistyObject
70
  {
71
  public static final int MESH_NICE = 0;
72
  public static final int MESH_FAST = 1;
73

    
74
  public static final int MODE_ICON = 0;
75
  public static final int MODE_NORM = 1;
76

    
77
  public static final int COLOR_YELLOW   = 0xffffff00;
78
  public static final int COLOR_WHITE    = 0xffffffff;
79
  public static final int COLOR_BLUE     = 0xff0000ff;
80
  public static final int COLOR_GREEN    = 0xff00bb00;
81
  public static final int COLOR_RED      = 0xff990000;
82
  public static final int COLOR_ORANGE   = 0xffff6200;
83
  public static final int COLOR_GREY     = 0xff727c7b;
84
  public static final int COLOR_VIOLET   = 0xff7700bb;
85
  public static final int COLOR_STROKE   = 0xff000000;
86
  public static final int COLOR_INTERNAL = 0xff000000;
87

    
88
  public static final int TEXTURE_HEIGHT = 256;
89
  static final int NUM_STICKERS_IN_ROW = 4;
90

    
91
  public static final float SQ2 = (float)Math.sqrt(2);
92
  public static final float SQ3 = (float)Math.sqrt(3);
93
  public static final float SQ5 = (float)Math.sqrt(5);
94
  public static final float SQ6 = (float)Math.sqrt(6);
95

    
96
  private static final float MAX_SIZE_CHANGE = 1.35f;
97
  private static final float MIN_SIZE_CHANGE = 0.75f;
98

    
99
  private static final Static3D CENTER = new Static3D(0,0,0);
100
  private static final int POST_ROTATION_MILLISEC = 500;
101

    
102
  protected float[][] mStickerCoords;
103
  protected Static4D[] mObjectQuats;
104
  int mNumAxis, mMaxNumLayers;
105

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

    
159
  //////////////////// SOLVED1 ////////////////////////
160

    
161
  private int[] mFaceMap;
162
  private int[][] mScramble;
163
  private int[] mColors;
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
  TwistyObject(InputStream jsonStream, int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream meshStream)
168
    {
169
    JsonReader reader = JsonReader.getInstance();
170
    reader.parseJsonFile(jsonStream);
171
    setReader(reader);
172

    
173
    mNumLayers = reader.getNumLayers();
174
    mSize      = reader.getSize();
175
    mInitData  = null;
176
    initialize(meshState,iconMode,quat,move,scale,meshStream,true);
177
    }
178

    
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180

    
181
  TwistyObject(InitData data, int meshState, int iconMode, float size, Static4D quat, Static3D move, float scale, InputStream meshStream)
182
    {
183
    mNumLayers = data.getNumLayers();
184
    mSize      = size;
185
    mInitData  = data;
186
    initialize(meshState,iconMode,quat,move,scale,meshStream,false);
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  private void initialize(int meshState, int iconMode, Static4D quat, Static3D move, float scale, InputStream stream, boolean fromJSON)
192
    {
193
    mIconMode = iconMode;
194
    mQuat = quat;
195
    mAxis = getRotationAxis();
196
    mInitScreenRatio = getScreenRatio();
197
    mSolvedFunctionIndex = getSolvedFunctionIndex();
198
    mBasicAngles = getBasicAngles();
199
    mObjectQuats = getQuats();
200
    mNumQuats = mObjectQuats.length;
201
    mOrigPos = getCubitPositions(mNumLayers);
202
    mNumPuzzleFaces = getNumFaces();
203
    mRowOffsets = new float[mNumPuzzleFaces][3];
204
    mTmp = new float[4];
205

    
206
    int numAxis = mAxis.length;
207
    mMaxNumLayers = -1;
208
    mCuts = getCuts(mNumLayers);
209
    mNumCuts = new int[numAxis];
210
    for(int i=0; i<numAxis; i++)
211
      {
212
      if( mMaxNumLayers<mNumLayers[i] ) mMaxNumLayers = mNumLayers[i];
213
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
214
      }
215

    
216
    mNumCubits = mOrigPos.length;
217
    mNumFaceColors = getNumFaceColors();
218
    mNumAxis = mAxis.length;
219
    mBelongs = new boolean[mNumCubits];
220

    
221
    int scramblingType = getScrambleType();
222
    ScrambleState[] states = getScrambleStates();
223
    mScrambler = new ObjectScrambler(scramblingType, mNumAxis,mNumLayers,states);
224

    
225
    boolean bandaged=false;
226

    
227
    for( int c=0; c<mNumCubits; c++)
228
      {
229
      if( mOrigPos[c].length>3 )
230
        {
231
        bandaged=true;
232
        break;
233
        }
234
      }
235
    mIsBandaged = bandaged;
236
    mQuatDebug = new int[mNumCubits];
237

    
238
    mRotationAngle= new Dynamic1D();
239
    mRotationAxis = new Static3D(1,0,0);
240
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
241

    
242
    mRotationAngleStatic = new Static1D(0);
243
    mRotationAngleMiddle = new Static1D(0);
244
    mRotationAngleFinal  = new Static1D(0);
245

    
246
    mObjectScale = new Static3D(scale,scale,scale);
247
    setObjectRatioNow(scale,720);
248

    
249
    mEffects = new DistortedEffects();
250
    createQuaternionEffects();
251

    
252
    MatrixEffectScale scaleEffect = new MatrixEffectScale(mObjectScale);
253
    MatrixEffectQuaternion quatEffect = new MatrixEffectQuaternion(mQuat, CENTER);
254
    MatrixEffectMove moveEffect = new MatrixEffectMove(move);
255

    
256
    boolean fromDMESH = (stream!=null && meshState==MESH_NICE);
257
    getQuatsAndShapes(fromDMESH,fromJSON);
258
    createMeshAndCubits(stream,meshState,fromDMESH);
259
    setUpTextures(fromDMESH,fromJSON);
260
    createDataStructuresForSolved();
261

    
262
    mEffects.apply(mRotateEffect);
263
    mEffects.apply(quatEffect);
264
    mEffects.apply(scaleEffect);
265
    mEffects.apply(moveEffect);
266

    
267
    mNode = new DistortedNode(mTexture,mEffects,mMesh);
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  private void createQuaternionEffects()
273
    {
274
    if( mNumQuats<=ObjectControl.MAX_QUATS )
275
      {
276
      mIsInMixupMode = false;
277

    
278
      for( int q=0; q<mNumQuats; q++)
279
        {
280
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mObjectQuats[q],CENTER);
281
        vq.setMeshAssociation(0,q);
282
        mEffects.apply(vq);
283
        }
284
      }
285
    else if( mNumCubits<=ObjectControl.MAX_QUATS )
286
      {
287
      mIsInMixupMode = true;
288
      mMixupModeQuats = new Static4D[mNumCubits];
289

    
290
      for( int q=0; q<mNumCubits; q++)
291
        {
292
        mMixupModeQuats[q] = new Static4D(mObjectQuats[0]);
293
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mMixupModeQuats[q],CENTER);
294
        vq.setMeshAssociation(0,q);
295
        mEffects.apply(vq);
296
        }
297
      }
298
    else
299
      {
300
      android.util.Log.e("D", "object has too many quaternions ("+mNumQuats+") or too many cubits ("+mNumCubits+")");
301
      }
302
    }
303

    
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305

    
306
  private Static3D getPos(float[] origPos)
307
    {
308
    int len = origPos.length/3;
309
    float sumX = 0.0f;
310
    float sumY = 0.0f;
311
    float sumZ = 0.0f;
312

    
313
    for(int i=0; i<len; i++)
314
      {
315
      sumX += origPos[3*i  ];
316
      sumY += origPos[3*i+1];
317
      sumZ += origPos[3*i+2];
318
      }
319

    
320
    sumX /= len;
321
    sumY /= len;
322
    sumZ /= len;
323

    
324
    return new Static3D(sumX,sumY,sumZ);
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

    
329
  private void createOuterFaces()
330
    {
331
    for(int v=0; v<mNumCubitVariants; v++)
332
      {
333
      int[][] indices = mShapes[v].getVertIndices();
334
      int faces = indices.length;
335
      mVariantFaceIsOuter[v] = new int[faces];
336
      }
337

    
338
    for( int cubit=0; cubit<mNumCubits; cubit++)
339
      {
340
      int variant = getCubitVariant(cubit,mNumLayers);
341
      int[][] indices = mShapes[variant].getVertIndices();
342
      int numFaces = indices.length;
343

    
344
      for(int face=0; face<numFaces; face++)
345
        if( getCubitFaceColor(cubit,face)>=0 )
346
          {
347
          mVariantFaceIsOuter[variant][face] = 1;
348
          }
349
      }
350
    }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

    
354
  private void getQuatsAndShapes(boolean fromDMESH, boolean fromJSON)
355
    {
356
    mNumCubitVariants = getNumCubitVariants(mNumLayers);
357

    
358
    if( !fromDMESH || !fromJSON )
359
      {
360
      FactoryCubit factory = FactoryCubit.getInstance();
361
      factory.clear();
362

    
363
      mOrigQuat = new Static4D[mNumCubits];
364
      for(int i=0; i<mNumCubits; i++) mOrigQuat[i] = getCubitQuats(i,mNumLayers);
365

    
366
      mShapes = new ObjectShape[mNumCubitVariants];
367
      for(int i=0; i<mNumCubitVariants; i++) mShapes[i] = getObjectShape(i);
368
      mNumCubitFaces = ObjectShape.computeNumComponents(mShapes);
369
      mVariantFaceIsOuter = new int[mNumCubitVariants][];
370

    
371
      if( !fromJSON )
372
        {
373
        mCubitFaceColors = ObjectShape.computeColors(mShapes,mOrigPos,mOrigQuat,this);
374
        createOuterFaces();
375
        }
376

    
377
      if( fromDMESH )
378
        {
379
        for(int i=0; i<mNumCubitVariants; i++) factory.createNewFaceTransform(mShapes[i], mVariantFaceIsOuter[i]);
380
        }
381
      }
382
    }
383

    
384
///////////////////////////////////////////////////////////////////////////////////////////////////
385

    
386
  private void createMeshAndCubits(InputStream stream, int meshState, boolean fromDMESH)
387
    {
388
    mCubits = new Cubit[mNumCubits];
389

    
390
    if( fromDMESH )
391
      {
392
      DataInputStream dos = new DataInputStream(stream);
393
      mMesh = new MeshFile(dos);
394

    
395
      try
396
        {
397
        stream.close();
398
        }
399
      catch(IOException e)
400
        {
401
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
402
        }
403
      }
404
    else
405
      {
406
      MeshBase[] cubitMesh = new MeshBase[mNumCubits];
407

    
408
      for(int i=0; i<mNumCubits; i++)
409
        {
410
        cubitMesh[i] = createCubitMesh(i,mNumLayers,meshState,mNumCubitFaces);
411
        Static3D pos = getPos(mOrigPos[i]);
412
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
413
        }
414

    
415
      mMesh = new MeshJoined(cubitMesh);
416
      }
417

    
418
    for(int i=0; i<mNumCubits; i++)
419
      {
420
      mCubits[i] = new Cubit(this,mOrigPos[i],mNumAxis,i);
421
      setCubitQuat(i,mCubits[i].computeAssociation(),0);
422
      }
423
    }
424

    
425
///////////////////////////////////////////////////////////////////////////////////////////////////
426

    
427
  private MeshBase createCubitMesh(int cubit, int[] numLayers, int meshState, int numComponents)
428
    {
429
    int variant = getCubitVariant(cubit,numLayers);
430

    
431
    if( mMeshes==null ) mMeshes = new MeshBase[mNumCubitVariants];
432

    
433
    if( mMeshes[variant]==null )
434
      {
435
      ObjectFaceShape faceShape = getObjectFaceShape(variant);
436
      FactoryCubit factory = FactoryCubit.getInstance();
437
      factory.createNewFaceTransform(mShapes[variant],mVariantFaceIsOuter[variant]);
438
      mMeshes[variant] = factory.createRoundedSolid(mShapes[variant],faceShape,meshState, numComponents);
439
      }
440

    
441
    MeshBase mesh = mMeshes[variant].copy(true);
442
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( mOrigQuat[cubit], CENTER );
443
    mesh.apply(quat,0xffffffff,0);
444

    
445
    return mesh;
446
    }
447

    
448
///////////////////////////////////////////////////////////////////////////////////////////////////
449

    
450
  private void setUpTextures(boolean fromDMESH, boolean fromJSON)
451
    {
452
    mTexture = new DistortedTexture();
453

    
454
    if( fromJSON )
455
      {
456
      mNumStickerTypes = getNumStickerTypes();
457
      mNumCubitFaces = getNumCubitFaces();
458
      }
459
    else
460
      {
461
      FactoryCubit factory = FactoryCubit.getInstance();
462
      mStickerCoords   = factory.getStickerCoords();
463
      mStickerVariants = factory.getStickerVariants();
464
      mStickerScales   = factory.getStickerScales();
465
      adjustStickerCoords();
466
      mNumStickerTypes = (mStickerCoords==null ? 0 : mStickerCoords.length);
467
      }
468

    
469
    mNumTextures= mNumFaceColors *mNumStickerTypes;
470
    mNumTexCols = NUM_STICKERS_IN_ROW;
471
    mNumTexRows = (mNumTextures+1)/NUM_STICKERS_IN_ROW;
472
    if( mNumTexCols*mNumTexRows < mNumTextures+1 ) mNumTexRows++;
473

    
474
    if( !fromDMESH || shouldResetTextureMaps() ) resetAllTextureMaps();
475
    setTexture();
476
    }
477

    
478
///////////////////////////////////////////////////////////////////////////////////////////////////
479

    
480
  private int getMultQuat(int index1, int index2)
481
    {
482
    if( mQuatMult==null )
483
      {
484
      mQuatMult = new int[mNumQuats][mNumQuats];
485

    
486
      for(int i=0; i<mNumQuats; i++)
487
        for(int j=0; j<mNumQuats; j++) mQuatMult[i][j] = -1;
488
      }
489

    
490
    if( mQuatMult[index1][index2]==-1 )
491
      {
492
      mQuatMult[index1][index2] = mulQuat(index1,index2);
493
      }
494

    
495
    return mQuatMult[index1][index2];
496
    }
497

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499

    
500
  public InitData getInitData()
501
    {
502
    return mInitData;
503
    }
504

    
505
///////////////////////////////////////////////////////////////////////////////////////////////////
506

    
507
  public boolean isInIconMode()
508
    {
509
    return mIconMode==MODE_ICON;
510
    }
511

    
512
///////////////////////////////////////////////////////////////////////////////////////////////////
513

    
514
  public int getVariantFaceColor(int variant, int face)
515
    {
516
    return face>=mStickerVariants[variant].length ? -1 : mStickerVariants[variant][face];
517
    }
518

    
519
///////////////////////////////////////////////////////////////////////////////////////////////////
520

    
521
  public boolean shouldResetTextureMaps()
522
    {
523
    return false;
524
    }
525

    
526
///////////////////////////////////////////////////////////////////////////////////////////////////
527

    
528
  private void createDataStructuresForSolved()
529
    {
530
    mTmpQuats = new int[mNumQuats];
531
    mSolvedQuats = getSolvedQuats();
532
    }
533

    
534
///////////////////////////////////////////////////////////////////////////////////////////////////
535
// This is used to build internal data structures for the generic 'isSolved()'
536
//
537
// if this is an internal cubit (all faces black): return -1
538
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
539
// Color index, i.e. the index into the 'FACE_COLORS' table.
540
// else (edge or corner cubit, more than one non-black face): return -2.
541

    
542
  protected int retCubitSolvedStatus(int cubit)
543
    {
544
    int numNonBlack=0, nonBlackIndex=-1, varColor, cubColor;
545
    int variant = getCubitVariant(cubit,mNumLayers);
546

    
547
    for(int face=0; face<mNumCubitFaces; face++)
548
      {
549
      varColor = getVariantFaceColor(variant,face);
550
      int numFaces = mCubitFaceColors[cubit].length;
551
      cubColor = face<numFaces ? mCubitFaceColors[cubit][face] : -1;
552

    
553
      if( varColor>=0 && cubColor>=0 )
554
        {
555
        numNonBlack++;
556
        nonBlackIndex = cubColor;
557
        }
558
      }
559

    
560
    if( numNonBlack==0 ) return -1;
561
    if( numNonBlack>=2 ) return -2;
562

    
563
    return nonBlackIndex;
564
    }
565

    
566
///////////////////////////////////////////////////////////////////////////////////////////////////
567

    
568
  private boolean sticksOut(Static3D[] faceAxis, float[] dist, float x, float y, float z )
569
    {
570
    final float MAXERR = 0.05f;
571
    int numAxis = dist.length;
572

    
573
    for(int i=0; i<numAxis; i++)
574
      {
575
      Static3D ax = faceAxis[i];
576
      float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
577
      if( len>mSize*dist[i]+MAXERR ) return true;
578
      }
579

    
580
    return false;
581
    }
582

    
583
///////////////////////////////////////////////////////////////////////////////////////////////////
584

    
585
  private boolean doesNotStickOut(int variant, float px, float py, float pz, float[] tmp, Static4D quat)
586
    {
587
    float[][] vertices = mShapes[variant].getVertices();
588
    Static3D[] axis = getFaceAxis();
589
    float[] dist3D = getDist3D(mNumLayers);
590

    
591
    for( float[] vertex : vertices)
592
      {
593
      float x = vertex[0];
594
      float y = vertex[1];
595
      float z = vertex[2];
596

    
597
      QuatHelper.rotateVectorByQuat(tmp, x, y, z, 1, quat);
598

    
599
      float mx = tmp[0] + px;
600
      float my = tmp[1] + py;
601
      float mz = tmp[2] + pz;
602

    
603
      if( sticksOut(axis, dist3D, mx,my,mz) ) return false;
604
      }
605

    
606
    return true;
607
    }
608

    
609
///////////////////////////////////////////////////////////////////////////////////////////////////
610

    
611
  private float computeAvg(float[] pos, int offset)
612
    {
613
    int len = pos.length/3;
614
    float ret=0.0f;
615
    for(int i=0; i<len; i++) ret += pos[3*i+offset];
616
    ret /= len;
617

    
618
    return ret;
619
    }
620

    
621
///////////////////////////////////////////////////////////////////////////////////////////////////
622

    
623
  protected void displayCubitQuats()
624
    {
625
    StringBuilder builder = new StringBuilder();
626
    float[] tmp = new float[4];
627
    float ERR = 0.01f;
628

    
629
    for(int cubit=0; cubit<mNumCubits; cubit++)
630
      {
631
      builder.append(cubit);
632
      builder.append(" : ");
633

    
634
      int refCubit,variant = getCubitVariant(cubit,mNumLayers);
635

    
636
      for(refCubit=0; refCubit<mNumCubits; refCubit++)
637
        {
638
        if( getCubitVariant(refCubit,mNumLayers)==variant ) break;
639
        }
640

    
641
      float[] curpos = mOrigPos[cubit];
642
      float[] refpos = mOrigPos[refCubit];
643
      float refX = computeAvg(refpos,0);
644
      float refY = computeAvg(refpos,1);
645
      float refZ = computeAvg(refpos,2);
646
      float curX = computeAvg(curpos,0);
647
      float curY = computeAvg(curpos,1);
648
      float curZ = computeAvg(curpos,2);
649

    
650
      for(int quat=0; quat<mNumQuats; quat++)
651
        {
652
        QuatHelper.rotateVectorByQuat(tmp,refX,refY,refZ,0,mObjectQuats[quat]);
653

    
654
        float dx = tmp[0]-curX;
655
        float dy = tmp[1]-curY;
656
        float dz = tmp[2]-curZ;
657

    
658
        if( dx>-ERR && dx<ERR && dy>-ERR && dy<ERR && dz>-ERR && dz<ERR )
659
          {
660
          if( doesNotStickOut(variant,curX,curY,curZ,tmp,mObjectQuats[quat]) )
661
            {
662
            builder.append(quat);
663
            builder.append(',');
664
            }
665
          else
666
            {
667
            android.util.Log.e("D", "cubit: "+cubit+" quat: "+quat+" : center correct, but shape sticks out");
668
            }
669
          }
670
        }
671

    
672
      builder.append('\n');
673
      }
674

    
675
    android.util.Log.e("D", "cubitQuats: \n"+builder.toString() );
676
    }
677

    
678
///////////////////////////////////////////////////////////////////////////////////////////////////
679

    
680
  protected int[] buildSolvedQuats(Static3D faceAx)
681
    {
682
    final float MAXD = 0.0001f;
683
    float x = faceAx.get0();
684
    float y = faceAx.get1();
685
    float z = faceAx.get2();
686
    float a,dx,dy,dz,qx,qy,qz;
687
    Static4D quat;
688
    int place = 0;
689

    
690
    for(int q=1; q<mNumQuats; q++)
691
      {
692
      quat = mObjectQuats[q];
693
      qx = quat.get0();
694
      qy = quat.get1();
695
      qz = quat.get2();
696

    
697
           if( x!=0.0f ) { a = qx/x; }
698
      else if( y!=0.0f ) { a = qy/y; }
699
      else               { a = qz/z; }
700

    
701
      dx = a*x-qx;
702
      dy = a*y-qy;
703
      dz = a*z-qz;
704

    
705
      if( dx>-MAXD && dx<MAXD && dy>-MAXD && dy<MAXD && dz>-MAXD && dz<MAXD )
706
        {
707
        mTmpQuats[place++] = q;
708
        }
709
      }
710

    
711
    if( place!=0 )
712
      {
713
      int[] ret = new int[place];
714
      System.arraycopy(mTmpQuats,0,ret,0,place);
715
      return ret;
716
      }
717

    
718
    return null;
719
    }
720

    
721
///////////////////////////////////////////////////////////////////////////////////////////////////
722

    
723
  public int getCubitRotationType(int cubit)
724
    {
725
    return Cubit.TYPE_NORMAL;
726
    }
727

    
728
///////////////////////////////////////////////////////////////////////////////////////////////////
729

    
730
  float[] getTrackingPoint(int cubitIndex, int cubitType)
731
    {
732
    if( cubitType!=Cubit.TYPE_NORMAL )
733
      {
734
      int variant = getCubitVariant(cubitIndex,mNumLayers);
735

    
736
      // object must have been created from JSON
737
      if( mVariantFaceIsOuter==null || mVariantFaceIsOuter[variant]==null )
738
        {
739
        mVariantFaceIsOuter = getVariantFaceIsOuter();
740
        }
741
      if( mShapes==null )
742
        {
743
        mShapes = new ObjectShape[mNumCubitVariants];
744
        }
745
      if( mShapes[variant]==null )
746
        {
747
        mShapes[variant] = getObjectShape(variant);
748
        }
749
      if( mOrigQuat==null )
750
        {
751
        mOrigQuat = new Static4D[mNumCubits];
752
        }
753
      if( mOrigQuat[cubitIndex]==null )
754
        {
755
        mOrigQuat[cubitIndex] = getCubitQuats(cubitIndex,mNumLayers);
756
        }
757

    
758
      int[][] indices = mShapes[variant].getVertIndices();
759
      int outer=-1, faces = indices.length;
760

    
761
      for(int i=0; i<faces; i++)
762
        {
763
        if( mVariantFaceIsOuter[variant][i]==1 )
764
          {
765
          outer=i;
766
          break;
767
          }
768
        }
769

    
770
      if( outer>=0 )
771
        {
772
        int vertIndex = indices[outer][0];
773
        float[] vertices = mShapes[variant].getVertices()[vertIndex];
774
        float[] ret = new float[3];
775
        float[] curpos = mOrigPos[cubitIndex];
776
        Static4D quat = mOrigQuat[cubitIndex];
777
        QuatHelper.rotateVectorByQuat(mTmp, vertices[0], vertices[1], vertices[2], 1, quat);
778

    
779
        ret[0] = mTmp[0]+computeAvg(curpos,0);
780
        ret[1] = mTmp[1]+computeAvg(curpos,1);
781
        ret[2] = mTmp[2]+computeAvg(curpos,2);
782

    
783
        return ret;
784
        }
785
      else
786
        {
787
        android.util.Log.e("D", "Error in getTrackingPoint: no outer face??");
788
        }
789
      }
790

    
791
    return null;
792
    }
793

    
794
///////////////////////////////////////////////////////////////////////////////////////////////////
795

    
796
  public int computeCurrentPuzzleFace(int type, float[] vertex)
797
    {
798
    if( type!=Cubit.TYPE_NORMAL )
799
      {
800
      Static3D[] axis = getFaceAxis();
801
      float[] dist3D = getDist3D(mNumLayers);
802
      final float MAXERR = 0.98f;
803
      int numAxis = axis.length;
804
      float x = vertex[0];
805
      float y = vertex[1];
806
      float z = vertex[2];
807

    
808
      for(int i=0; i<numAxis; i++)
809
        {
810
        Static3D ax = axis[i];
811
        float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
812
        if( len>mSize*dist3D[i]*MAXERR ) return i;
813
        }
814
      }
815

    
816
    return -1;
817
    }
818

    
819
///////////////////////////////////////////////////////////////////////////////////////////////////
820

    
821
  public float[] getCubitRowOffset(int cubitIndex)
822
    {
823
    return null;
824
    }
825

    
826
///////////////////////////////////////////////////////////////////////////////////////////////////
827

    
828
  void setRotationRowOffset(int puzzleFace, float[] offset)
829
    {
830
    mRowOffsets[puzzleFace][0] = offset[0];
831
    mRowOffsets[puzzleFace][1] = offset[1];
832
    mRowOffsets[puzzleFace][2] = offset[2];
833
    }
834

    
835
///////////////////////////////////////////////////////////////////////////////////////////////////
836

    
837
  public int[][] getSolvedQuats()
838
    {
839
    int[] groups = new int[mNumCubits];
840
    int numGroups = 1;
841
    int numFirst  = 0;
842

    
843
    for(int cubit=0; cubit<mNumCubits; cubit++)
844
      {
845
      groups[cubit] = retCubitSolvedStatus(cubit);
846
      if( groups[cubit]>=0 ) numGroups++;
847
      else                   numFirst++;
848
      }
849

    
850
    int firstIndex = 1;
851
    int groupIndex = 1;
852
    int[][] solvedQuats = new int[numGroups][];
853
    solvedQuats[0] = new int[1+numFirst];
854
    solvedQuats[0][0] = numFirst;
855
    Static3D[] axis = getFaceAxis();
856

    
857
    for(int cubit=0; cubit<mNumCubits; cubit++)
858
      {
859
      int group = groups[cubit];
860

    
861
      if( group<0 )
862
        {
863
        solvedQuats[0][firstIndex] = cubit;
864
        firstIndex++;
865
        }
866
      else
867
        {
868
        int[] quats = buildSolvedQuats(axis[group]);
869
        int len = quats==null ? 0 : quats.length;
870
        solvedQuats[groupIndex] = new int[2+len];
871
        solvedQuats[groupIndex][0] = 1;
872
        solvedQuats[groupIndex][1] = cubit;
873
        for(int i=0; i<len; i++) solvedQuats[groupIndex][i+2] = quats[i];
874
        groupIndex++;
875
        }
876
      }
877
/*
878
    String dbg = "SOLVED GROUPS:\n";
879

    
880
    for(int g=0; g<numGroups; g++)
881
      {
882
      int len = solvedQuats[g].length;
883
      for(int i=0; i<len; i++) dbg += (" "+solvedQuats[g][i]);
884
      dbg+="\n";
885
      }
886

    
887
    android.util.Log.e("D", dbg);
888
*/
889
    return solvedQuats;
890
    }
891

    
892
///////////////////////////////////////////////////////////////////////////////////////////////////
893

    
894
  public int getSolvedFunctionIndex()
895
    {
896
    return 0;
897
    }
898

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

    
904
  private boolean isSolvedCentersOnly()
905
    {
906
    int numGroups = mSolvedQuats.length;
907

    
908
    for(int group=1; group<numGroups; group++)
909
      {
910
      int numEntries= mSolvedQuats[group].length;
911
      int numCubits = mSolvedQuats[group][0];
912
      int firstCubit= mSolvedQuats[group][1];
913
      int firstQuat = mCubits[firstCubit].mQuatIndex;
914

    
915
      for(int cubit=2; cubit<=numCubits; cubit++)
916
        {
917
        int currCubit= mSolvedQuats[group][cubit];
918
        int currQuat = mCubits[currCubit].mQuatIndex;
919
        boolean isGood= (firstQuat==currQuat);
920

    
921
        for(int q=numCubits+1; !isGood && q<numEntries; q++)
922
          {
923
          int quat = mSolvedQuats[group][q];
924
          if( firstQuat == getMultQuat(currQuat,quat) ) isGood = true;
925
          }
926

    
927
        if( !isGood ) return false;
928
        }
929
      }
930

    
931
    return true;
932
    }
933

    
934
///////////////////////////////////////////////////////////////////////////////////////////////////
935

    
936
  private boolean isSolved0()
937
    {
938
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly();
939

    
940
    for( int[] solvedQuat : mSolvedQuats )
941
      {
942
      int numCubits = solvedQuat[0];
943
      int firstCubit= solvedQuat[1];
944
      int quat = mCubits[firstCubit].mQuatIndex;
945

    
946
      for( int cubit=2; cubit<=numCubits; cubit++ )
947
        {
948
        int c = solvedQuat[cubit];
949
        if( quat != mCubits[c].mQuatIndex ) return false;
950
        }
951
      }
952

    
953
    int cubit= mSolvedQuats[0][1];
954
    int quat0= mCubits[cubit].mQuatIndex;
955
    int numGroups = mSolvedQuats.length;
956

    
957
    for(int group=1; group<numGroups; group++)
958
      {
959
      int firstCubit= mSolvedQuats[group][1];
960
      int currQuat  = mCubits[firstCubit].mQuatIndex;
961

    
962
      if( quat0==currQuat ) continue;
963

    
964
      boolean isGood= false;
965
      int numEntries= mSolvedQuats[group].length;
966
      int numCubits = mSolvedQuats[group][0];
967

    
968
      for(int q=numCubits+1; q<numEntries; q++)
969
        {
970
        int quat = mSolvedQuats[group][q];
971

    
972
        if( quat0 == getMultQuat(currQuat,quat) )
973
          {
974
          isGood = true;
975
          break;
976
          }
977
        }
978

    
979
      if( !isGood ) return false;
980
      }
981

    
982
    return true;
983
    }
984

    
985
///////////////////////////////////////////////////////////////////////////////////////////////////
986

    
987
  private int computeScramble(int quatNum, int centerNum)
988
    {
989
    float MAXDIFF = 0.01f;
990
    float[] center= mOrigPos[centerNum];
991
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
992
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
993

    
994
    float x = result.get0();
995
    float y = result.get1();
996
    float z = result.get2();
997

    
998
    for(int c=0; c<mNumCubits; c++)
999
      {
1000
      float[] cent = mOrigPos[c];
1001

    
1002
      float qx = cent[0] - x;
1003
      float qy = cent[1] - y;
1004
      float qz = cent[2] - z;
1005

    
1006
      if( qx>-MAXDIFF && qx<MAXDIFF &&
1007
          qy>-MAXDIFF && qy<MAXDIFF &&
1008
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
1009
      }
1010

    
1011
    return -1;
1012
    }
1013

    
1014
///////////////////////////////////////////////////////////////////////////////////////////////////
1015
// Dino4 uses this. It is solved if and only if groups of cubits
1016
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
1017
// or
1018
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
1019
// are all the same color.
1020

    
1021
  private boolean isSolved1()
1022
    {
1023
    if( mScramble==null )
1024
      {
1025
      mScramble = new int[mNumQuats][mNumCubits];
1026
      mColors   = new int[mNumCubits];
1027

    
1028
      for(int q=0; q<mNumQuats; q++)
1029
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
1030
      }
1031

    
1032
    if( mFaceMap==null )
1033
      {
1034
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
1035
      }
1036

    
1037
    for(int c=0; c<mNumCubits; c++)
1038
      {
1039
      int index = mScramble[mCubits[c].mQuatIndex][c];
1040
      mColors[index] = mFaceMap[c];
1041
      }
1042

    
1043
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
1044
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
1045
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
1046

    
1047
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
1048
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
1049
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
1050

    
1051
    return false;
1052
    }
1053

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

    
1056
  int computeRow(float[] pos, int axisIndex, int cubitType, int puzzleFace)
1057
    {
1058
    int ret=0;
1059
    int len = pos.length / 3;
1060
    Static3D axis = mAxis[axisIndex];
1061
    float axisX = axis.get0();
1062
    float axisY = axis.get1();
1063
    float axisZ = axis.get2();
1064
    float casted, xoff=0, yoff=0, zoff=0;
1065

    
1066
    if( cubitType!=Cubit.TYPE_NORMAL )
1067
      {
1068
      xoff = mRowOffsets[puzzleFace][0];
1069
      yoff = mRowOffsets[puzzleFace][1];
1070
      zoff = mRowOffsets[puzzleFace][2];
1071
      }
1072

    
1073
    for(int i=0; i<len; i++)
1074
      {
1075
      casted = (pos[3*i]+xoff)*axisX + (pos[3*i+1]+yoff)*axisY + (pos[3*i+2]+zoff)*axisZ;
1076
      ret |= computeSingleRow(axisIndex,casted);
1077
      }
1078

    
1079
    return ret;
1080
    }
1081

    
1082
///////////////////////////////////////////////////////////////////////////////////////////////////
1083

    
1084
  private int computeSingleRow(int axisIndex,float casted)
1085
    {
1086
    int num = mNumCuts[axisIndex];
1087

    
1088
    for(int i=0; i<num; i++)
1089
      {
1090
      if( casted<mCuts[axisIndex][i] ) return (1<<i);
1091
      }
1092

    
1093
    return (1<<num);
1094
    }
1095

    
1096
///////////////////////////////////////////////////////////////////////////////////////////////////
1097

    
1098
  private boolean wasRotateApplied()
1099
    {
1100
    return mEffects.exists(mRotateEffect.getID());
1101
    }
1102

    
1103
///////////////////////////////////////////////////////////////////////////////////////////////////
1104

    
1105
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
1106
    {
1107
    return (mCubits[cubit].getRotRow(axis) & rowBitmap) != 0;
1108
    }
1109

    
1110
///////////////////////////////////////////////////////////////////////////////////////////////////
1111
// note the minus in front of the sin() - we rotate counterclockwise
1112
// when looking towards the direction where the axis increases in values.
1113

    
1114
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
1115
    {
1116
    Static3D axis = mAxis[axisIndex];
1117

    
1118
    while( angleInDegrees<0 ) angleInDegrees += 360;
1119
    angleInDegrees %= 360;
1120
    
1121
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
1122
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
1123

    
1124
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
1125
    }
1126

    
1127
///////////////////////////////////////////////////////////////////////////////////////////////////
1128

    
1129
  private synchronized void setupPosition(int[][] moves)
1130
    {
1131
    if( moves!=null )
1132
      {
1133
      Static4D quat;
1134
      int index, axis, row, rowBitmap, angle;
1135

    
1136
      for(int[] move: moves)
1137
        {
1138
        axis     = move[0];
1139
        rowBitmap= computeBitmapFromRow( move[1],axis );
1140
        row      = computeRowFromBitmap( move[1] );
1141
        angle    = move[2]*(360/mBasicAngles[axis][row]);   // this assumes that all layers from
1142
                                                            // the bitmap have the same BasicAngle.
1143
                                                            // at the moment this is always true as
1144
                                                            // there are no bandaged objects with
1145
                                                            // different per-layer BasicAngles.
1146
        quat = makeQuaternion(axis,angle);
1147

    
1148
        for(int j=0; j<mNumCubits; j++)
1149
          {
1150
          mBelongs[j] = belongsToRotation(j,axis,rowBitmap);
1151
          if( mBelongs[j] ) mCubits[j].rotateCubit(quat);
1152
          }
1153

    
1154
        recomputeFaceOffsets();
1155

    
1156
        for(int j=0; j<mNumCubits; j++)
1157
          {
1158
          if( mBelongs[j] )
1159
            {
1160
            index = mCubits[j].postRotateCubit(quat);
1161
            setCubitQuat(j,mCubits[j].computeAssociation(),index);
1162
            }
1163
          else if( mCubits[j].getType()==Cubit.TYPE_FOLLOWER )
1164
            {
1165
            mCubits[j].computeRotationRow();
1166
            setCubitQuat(j,mCubits[j].computeAssociation(),mCubits[j].mQuatIndex);
1167
            }
1168
          }
1169
        }
1170
      }
1171
    }
1172

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

    
1175
  public int getScrambleType()
1176
    {
1177
    return 0;
1178
    }
1179

    
1180
///////////////////////////////////////////////////////////////////////////////////////////////////
1181

    
1182
  int computeBitmapFromRow(int rowBitmap, int axis)
1183
    {
1184
    if( mIsBandaged )
1185
      {
1186
      int bitmap, initBitmap=0;
1187

    
1188
      while( initBitmap!=rowBitmap )
1189
        {
1190
        initBitmap = rowBitmap;
1191

    
1192
        for(int cubit=0; cubit<mNumCubits; cubit++)
1193
          {
1194
          bitmap = mCubits[cubit].getRotRow(axis);
1195
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
1196
          }
1197
        }
1198
      }
1199

    
1200
    return rowBitmap;
1201
    }
1202

    
1203
///////////////////////////////////////////////////////////////////////////////////////////////////
1204

    
1205
  private int computeRowFromBitmap(int rowBitmap)
1206
    {
1207
    int index = 0;
1208

    
1209
    while(index<32)
1210
      {
1211
      if( (rowBitmap&0x1) != 0 ) return index;
1212
      rowBitmap>>=1;
1213
      index++;
1214
      }
1215
    return 0;
1216
    }
1217

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

    
1222
  void clampPos(float[] pos, int offset)
1223
    {
1224
    float currError, minError = Float.MAX_VALUE;
1225
    int minErrorIndex1 = -1;
1226
    int minErrorIndex2 = -1;
1227

    
1228
    float x = pos[offset  ];
1229
    float y = pos[offset+1];
1230
    float z = pos[offset+2];
1231

    
1232
    float xo,yo,zo;
1233

    
1234
    for(int i=0; i<mNumCubits; i++)
1235
      {
1236
      int len = mOrigPos[i].length / 3;
1237

    
1238
      for(int j=0; j<len; j++)
1239
        {
1240
        xo = mOrigPos[i][3*j  ];
1241
        yo = mOrigPos[i][3*j+1];
1242
        zo = mOrigPos[i][3*j+2];
1243

    
1244
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
1245

    
1246
        if( currError<minError )
1247
          {
1248
          minError = currError;
1249
          minErrorIndex1 = i;
1250
          minErrorIndex2 = j;
1251
          }
1252
        }
1253
      }
1254

    
1255
    if( minError< 0.1f ) // TODO: 0.1 ?
1256
      {
1257
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
1258
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
1259
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
1260
      }
1261
    }
1262

    
1263
///////////////////////////////////////////////////////////////////////////////////////////////////
1264
// remember about the double cover or unit quaternions!
1265

    
1266
  int mulQuat(int q1, int q2)
1267
    {
1268
    Static4D result = QuatHelper.quatMultiply(mObjectQuats[q1],mObjectQuats[q2]);
1269

    
1270
    float rX = result.get0();
1271
    float rY = result.get1();
1272
    float rZ = result.get2();
1273
    float rW = result.get3();
1274

    
1275
    final float MAX_ERROR = 0.1f;
1276
    float dX,dY,dZ,dW;
1277

    
1278
    for(int i=0; i<mNumQuats; i++)
1279
      {
1280
      dX = mObjectQuats[i].get0() - rX;
1281
      dY = mObjectQuats[i].get1() - rY;
1282
      dZ = mObjectQuats[i].get2() - rZ;
1283
      dW = mObjectQuats[i].get3() - rW;
1284

    
1285
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
1286
          dY<MAX_ERROR && dY>-MAX_ERROR &&
1287
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
1288
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
1289

    
1290
      dX = mObjectQuats[i].get0() + rX;
1291
      dY = mObjectQuats[i].get1() + rY;
1292
      dZ = mObjectQuats[i].get2() + rZ;
1293
      dW = mObjectQuats[i].get3() + rW;
1294

    
1295
      if( dX<MAX_ERROR && dX>-MAX_ERROR &&
1296
          dY<MAX_ERROR && dY>-MAX_ERROR &&
1297
          dZ<MAX_ERROR && dZ>-MAX_ERROR &&
1298
          dW<MAX_ERROR && dW>-MAX_ERROR  ) return i;
1299
      }
1300

    
1301
    return -1;
1302
    }
1303

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

    
1306
  private float getAngle()
1307
    {
1308
    int pointNum = mRotationAngle.getNumPoints();
1309

    
1310
    if( pointNum>=1 )
1311
      {
1312
      return mRotationAngle.getPoint(pointNum-1).get0();
1313
      }
1314
    else
1315
      {
1316
      mInterface.reportProblem("points in RotationAngle: "+pointNum, false);
1317
      return 0;
1318
      }
1319
    }
1320

    
1321
///////////////////////////////////////////////////////////////////////////////////////////////////
1322

    
1323
  void setLibInterface(ObjectLibInterface inter)
1324
    {
1325
    mInterface = inter;
1326
    }
1327

    
1328
///////////////////////////////////////////////////////////////////////////////////////////////////
1329

    
1330
  void initializeObject(int[][] moves)
1331
    {
1332
    solve();
1333
    setupPosition(moves);
1334
    }
1335

    
1336
///////////////////////////////////////////////////////////////////////////////////////////////////
1337

    
1338
  synchronized void removeRotationNow()
1339
    {
1340
    float angle = getAngle();
1341
    double nearestAngleInRadians = angle*Math.PI/180;
1342
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
1343
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
1344
    float axisX = mAxis[mCurrentRotAxis].get0();
1345
    float axisY = mAxis[mCurrentRotAxis].get1();
1346
    float axisZ = mAxis[mCurrentRotAxis].get2();
1347
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
1348

    
1349
    mRotationAngle.removeAll();
1350
    mRotationAngleStatic.set0(0);
1351

    
1352
    for(int i=0; i<mNumCubits; i++)
1353
      {
1354
      mBelongs[i] = belongsToRotation(i, mCurrentRotAxis,mRotRowBitmap);
1355
      if( mBelongs[i] ) mCubits[i].rotateCubit(quat);
1356
      }
1357

    
1358
    recomputeFaceOffsets();
1359

    
1360
    for(int i=0; i<mNumCubits; i++)
1361
      {
1362
      if( mBelongs[i] )
1363
        {
1364
        int index = mCubits[i].postRotateCubit(quat);
1365
        setCubitQuat(i,mCubits[i].computeAssociation(),index);
1366
        }
1367
      else if( mCubits[i].getType()==Cubit.TYPE_FOLLOWER )
1368
        {
1369
        mCubits[i].computeRotationRow();
1370
        setCubitQuat(i,mCubits[i].computeAssociation(),mCubits[i].mQuatIndex);
1371
        }
1372
      }
1373
    }
1374

    
1375
///////////////////////////////////////////////////////////////////////////////////////////////////
1376

    
1377
  private void recomputeFaceOffsets()
1378
    {
1379
    for(int i=0; i<mNumPuzzleFaces; i++)
1380
      {
1381
      mRowOffsets[i][0] =0;
1382
      mRowOffsets[i][1] =0;
1383
      mRowOffsets[i][2] =0;
1384
      }
1385

    
1386
    for(int i=0; i<mNumCubits; i++)
1387
      if( mCubits[i].getType()==Cubit.TYPE_DECIDER )
1388
        {
1389
        float[] offset = mCubits[i].getOffset();
1390
        int face = mCubits[i].getPuzzleFace();
1391
        mRowOffsets[face][0] = offset[0];
1392
        mRowOffsets[face][1] = offset[1];
1393
        mRowOffsets[face][2] = offset[2];
1394
        }
1395
    }
1396

    
1397
///////////////////////////////////////////////////////////////////////////////////////////////////
1398

    
1399
  long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
1400
    {
1401
    if( wasRotateApplied() )
1402
      {
1403
      float angle = getAngle();
1404
      mRotationAngleStatic.set0(angle);
1405
      mRotationAngleFinal.set0(nearestAngleInDegrees);
1406
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
1407

    
1408
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
1409
      mRotationAngle.resetToBeginning();
1410
      mRotationAngle.removeAll();
1411
      mRotationAngle.add(mRotationAngleStatic);
1412
      mRotationAngle.add(mRotationAngleMiddle);
1413
      mRotationAngle.add(mRotationAngleFinal);
1414
      mRotateEffect.notifyWhenFinished(listener);
1415

    
1416
      return mRotateEffect.getID();
1417
      }
1418

    
1419
    return 0;
1420
    }
1421

    
1422
///////////////////////////////////////////////////////////////////////////////////////////////////
1423

    
1424
  synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
1425
    {
1426
    if( wasRotateApplied() )
1427
      {
1428
      mCurrentRotAxis = axis;
1429
      mRotRowBitmap= computeBitmapFromRow( rowBitmap,axis );
1430

    
1431
      mRotationAngleStatic.set0(0.0f);
1432
      mRotationAxis.set( mAxis[axis] );
1433
      mRotationAngle.setDuration(durationMillis);
1434
      mRotationAngle.resetToBeginning();
1435
      mRotationAngle.add(new Static1D(0));
1436
      mRotationAngle.add(new Static1D(angle));
1437
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*mMaxNumLayers) , -1);
1438
      mRotateEffect.notifyWhenFinished(listener);
1439

    
1440
      return mRotateEffect.getID();
1441
      }
1442

    
1443
    return 0;
1444
    }
1445

    
1446
///////////////////////////////////////////////////////////////////////////////////////////////////
1447

    
1448
  void continueRotation(float angleInDegrees)
1449
    {
1450
    mRotationAngleStatic.set0(angleInDegrees);
1451
    }
1452

    
1453
///////////////////////////////////////////////////////////////////////////////////////////////////
1454

    
1455
  synchronized void beginNewRotation(int axis, int row )
1456
    {
1457
    if( axis<0 || axis>=mNumAxis )
1458
      {
1459
      android.util.Log.e("object", "invalid rotation axis: "+axis);
1460
      return;
1461
      }
1462
    if( row<0 || row>=mNumLayers[axis] )
1463
      {
1464
      android.util.Log.e("object", "invalid rotation row: "+row);
1465
      return;
1466
      }
1467

    
1468
    mCurrentRotAxis = axis;
1469
    mRotRowBitmap= computeBitmapFromRow( (1<<row),axis );
1470
    mRotationAngleStatic.set0(0.0f);
1471
    mRotationAxis.set( mAxis[axis] );
1472
    mRotationAngle.add(mRotationAngleStatic);
1473
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*mMaxNumLayers) , -1);
1474
    }
1475

    
1476
///////////////////////////////////////////////////////////////////////////////////////////////////
1477

    
1478
  void setTextureMap(int cubit, int face, int newColor)
1479
    {
1480
    final float ratioW = 1.0f/mNumTexCols;
1481
    final float ratioH = 1.0f/mNumTexRows;
1482
    final Static4D[] maps = new Static4D[mNumCubitFaces];
1483
    int row = (mNumTexRows-1) - newColor/mNumTexCols;
1484
    int col = newColor%mNumTexCols;
1485

    
1486
    maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1487
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1488
    }
1489

    
1490
///////////////////////////////////////////////////////////////////////////////////////////////////
1491

    
1492
  private int getCubitFaceColor(int cubit, int face)
1493
    {
1494
    int puzzleFace = getCubitFaceMap(cubit,face);
1495
    if( puzzleFace>=0 ) puzzleFace %= mNumFaceColors;
1496
    return puzzleFace;
1497
    }
1498

    
1499
///////////////////////////////////////////////////////////////////////////////////////////////////
1500

    
1501
  public int getCubitFaceMap(int cubit, int face)
1502
    {
1503
    int numFaces = mCubitFaceColors[cubit].length;
1504
    int puzzleFace = face<numFaces ? mCubitFaceColors[cubit][face] : -1;
1505
    return puzzleFace<0 ? -1 : puzzleFace;
1506
    }
1507

    
1508
///////////////////////////////////////////////////////////////////////////////////////////////////
1509

    
1510
  void resetAllTextureMaps()
1511
    {
1512
    final float ratioW = 1.0f/mNumTexCols;
1513
    final float ratioH = 1.0f/mNumTexRows;
1514
    int cubColor, varColor, color, variant, row, col;
1515

    
1516
    for(int cubit=0; cubit<mNumCubits; cubit++)
1517
      {
1518
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1519
      variant = getCubitVariant(cubit,mNumLayers);
1520

    
1521
      for(int face=0; face<mNumCubitFaces; face++)
1522
        {
1523
        cubColor = getCubitFaceColor(cubit,face);
1524
        varColor = getVariantFaceColor(variant,face);
1525
        color    = cubColor<0 || varColor<0 ? mNumTextures : varColor*mNumFaceColors + cubColor;
1526
        row      = (mNumTexRows-1) - color/mNumTexCols;
1527
        col      = color%mNumTexCols;
1528

    
1529
        maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1530
        }
1531

    
1532
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1533
      }
1534
    }
1535

    
1536
///////////////////////////////////////////////////////////////////////////////////////////////////
1537

    
1538
  void releaseResources()
1539
    {
1540
    mTexture.markForDeletion();
1541
    mMesh.markForDeletion();
1542
    mEffects.markForDeletion();
1543

    
1544
    for(int j=0; j<mNumCubits; j++)
1545
      {
1546
      mCubits[j].releaseResources();
1547
      }
1548
    }
1549

    
1550
///////////////////////////////////////////////////////////////////////////////////////////////////
1551

    
1552
  private void setCubitQuat(int cubit, int andAssociation, int equAssociation)
1553
    {
1554
    if( !mIsInMixupMode )
1555
      {
1556
      mMesh.setEffectAssociation(cubit,andAssociation,equAssociation);
1557
      }
1558
    else
1559
      {
1560
      mMesh.setEffectAssociation(cubit,andAssociation,cubit);
1561
      Static4D tmp = mObjectQuats[equAssociation];
1562
      mMixupModeQuats[cubit].set(tmp);
1563
      }
1564
    }
1565

    
1566
///////////////////////////////////////////////////////////////////////////////////////////////////
1567

    
1568
  synchronized void restorePreferences(SharedPreferences preferences)
1569
    {
1570
    boolean error = false;
1571
    String key = getShortName();
1572

    
1573
    for(int i=0; i<mNumCubits; i++)
1574
      {
1575
      mQuatDebug[i] = mCubits[i].restorePreferences(key,preferences);
1576

    
1577
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1578
        {
1579
        mCubits[i].rotateCubit(mObjectQuats[mQuatDebug[i]]);
1580
        }
1581
      else
1582
        {
1583
        error = true;
1584
        }
1585
      }
1586

    
1587
    for(int i=0; i<mNumCubits; i++)
1588
      {
1589
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1590
        {
1591
        mCubits[i].computeRotationRow();
1592
        setCubitQuat(i,mCubits[i].computeAssociation(),mQuatDebug[i]);
1593
        }
1594
      else
1595
        {
1596
        error = true;
1597
        }
1598
      }
1599

    
1600
    if( error )
1601
      {
1602
      for(int i=0; i<mNumCubits; i++)
1603
        {
1604
        mCubits[i].solve();
1605
        setCubitQuat(i,mCubits[i].computeAssociation(),0);
1606
        }
1607
      }
1608
    }
1609

    
1610
///////////////////////////////////////////////////////////////////////////////////////////////////
1611

    
1612
  void savePreferences(SharedPreferences.Editor editor)
1613
    {
1614
    String key = getShortName();
1615
    for(int i=0; i<mNumCubits; i++) mCubits[i].savePreferences(key,editor);
1616
    }
1617

    
1618
///////////////////////////////////////////////////////////////////////////////////////////////////
1619

    
1620
  public void removePreferences(SharedPreferences.Editor editor)
1621
    {
1622
    String key = getShortName();
1623
    for(int i=0; i<mNumCubits; i++) mCubits[i].removePreferences(key,editor);
1624
    }
1625

    
1626
///////////////////////////////////////////////////////////////////////////////////////////////////
1627

    
1628
  private float computeRadiusCorrection(float[] sticker, int curr, int len)
1629
    {
1630
    final float A = 0.8f;  // 0<A<1
1631

    
1632
    int prev = curr>0 ? curr-1 : len-1;
1633
    int next = curr<len-1 ? curr+1 : 0;
1634

    
1635
    float v1x = sticker[2*prev  ]-sticker[2*curr  ];
1636
    float v1y = sticker[2*prev+1]-sticker[2*curr+1];
1637
    float v2x = sticker[2*next  ]-sticker[2*curr  ];
1638
    float v2y = sticker[2*next+1]-sticker[2*curr+1];
1639

    
1640
    float len1= v1x*v1x+v1y*v1y;
1641
    float len2= v2x*v2x+v2y*v2y;
1642

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

    
1645
    return 1-A*cos;
1646
    }
1647

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

    
1650
  public ObjectSticker retSticker(int sticker)
1651
    {
1652
    if( mStickers==null )
1653
      {
1654
      float rad = getStickerRadius();
1655
      float str = getStickerStroke();
1656
      float[][] angles = getStickerAngles();
1657
      int numStickers = mStickerCoords.length;
1658
      mStickers = new ObjectSticker[numStickers];
1659

    
1660
      for(int s=0; s<numStickers; s++)
1661
        {
1662
        float scale = mStickerScales.length>s ? mStickerScales[s] : 1.0f;
1663
        float radius = rad / scale;
1664
        float stroke = str / scale;
1665
        int len = mStickerCoords[s].length/2;
1666
        float[] radii = new float[len];
1667
        for(int r=0; r<len; r++) radii[r] = radius*computeRadiusCorrection(mStickerCoords[s],r,len);
1668
        mStickers[s] = new ObjectSticker(mStickerCoords[s],angles==null ? null : angles[s],radii,stroke);
1669
        }
1670
      }
1671

    
1672
    return mStickers[sticker];
1673
    }
1674

    
1675
///////////////////////////////////////////////////////////////////////////////////////////////////
1676
// some objects (currently Kilominx,Ivy,Rex) might want to change the stickers.
1677

    
1678
  public void adjustStickerCoords()
1679
    {
1680

    
1681
    }
1682

    
1683
///////////////////////////////////////////////////////////////////////////////////////////////////
1684

    
1685
  public Static4D[] getQuats()
1686
    {
1687
    if( mObjectQuats==null )
1688
      {
1689
      mObjectQuats = QuatGroupGenerator.computeGroup(mAxis,mBasicAngles);
1690
      }
1691

    
1692
    return mObjectQuats;
1693
    }
1694

    
1695
///////////////////////////////////////////////////////////////////////////////////////////////////
1696

    
1697
  public int[][] getVariantFaceIsOuter()
1698
    {
1699
    return mVariantFaceIsOuter;
1700
    }
1701

    
1702
///////////////////////////////////////////////////////////////////////////////////////////////////
1703

    
1704
  public int getInternalColor()
1705
    {
1706
    return COLOR_INTERNAL;
1707
    }
1708

    
1709
///////////////////////////////////////////////////////////////////////////////////////////////////
1710
// the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
1711

    
1712
  private void createTexture()
1713
    {
1714
    Paint paint = new Paint();
1715
    mBitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_4444);
1716
    Canvas canvas = new Canvas(mBitmap);
1717

    
1718
    paint.setAntiAlias(true);
1719
    paint.setTextAlign(Paint.Align.CENTER);
1720
    paint.setStyle(Paint.Style.FILL);
1721

    
1722
    paint.setColor(getInternalColor());
1723
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
1724

    
1725
    int texture = 0;
1726
    FactorySticker factory = FactorySticker.getInstance();
1727

    
1728
    for(int row=0; row<mNumTexRows; row++)
1729
      for(int col=0; col<mNumTexCols; col++)
1730
        {
1731
        if( texture>=mNumTextures ) break;
1732
        ObjectSticker sticker = retSticker(texture/mNumFaceColors);
1733
        int color = getColor(texture% mNumFaceColors);
1734
        factory.drawRoundedPolygon(canvas, paint, col*TEXTURE_HEIGHT, (mNumTexRows-row)*TEXTURE_HEIGHT, color, sticker);
1735
        texture++;
1736
        }
1737
    }
1738

    
1739
///////////////////////////////////////////////////////////////////////////////////////////////////
1740

    
1741
  void setTexture()
1742
    {
1743
    if( mBitmap==null ) createTexture();
1744

    
1745
    if( !mTexture.setTextureAlreadyInverted(mBitmap) )
1746
      {
1747
      int max = DistortedLibrary.getMaxTextureSize();
1748
      mInterface.reportProblem("failed to set texture of size "+mBitmap.getWidth()+"x"+mBitmap.getHeight()+" max is "+max, true);
1749
      }
1750
    }
1751

    
1752
///////////////////////////////////////////////////////////////////////////////////////////////////
1753

    
1754
  void setObjectRatioNow(float sc, int nodeSize)
1755
    {
1756
    mObjectScreenRatio = sc;
1757
    float scale = mObjectScreenRatio*mInitScreenRatio*nodeSize/mSize;
1758
    mObjectScale.set(scale,scale,scale);
1759

    
1760
    if( mTouchControl ==null ) mTouchControl = getTouchControl();
1761
    mTouchControl.setObjectRatio(mObjectScreenRatio*mInitScreenRatio);
1762
    }
1763

    
1764
///////////////////////////////////////////////////////////////////////////////////////////////////
1765

    
1766
  void setObjectRatio(float sizeChange, int nodeSize)
1767
    {
1768
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1769

    
1770
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1771
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1772

    
1773
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1774
    }
1775

    
1776
///////////////////////////////////////////////////////////////////////////////////////////////////
1777

    
1778
  void setNodeSize(int nodeSize)
1779
    {
1780
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1781
    }
1782

    
1783
///////////////////////////////////////////////////////////////////////////////////////////////////
1784

    
1785
  public float getRatio()
1786
    {
1787
    return mObjectScreenRatio;
1788
    }
1789

    
1790
///////////////////////////////////////////////////////////////////////////////////////////////////
1791

    
1792
  public float getObjectRatio()
1793
    {
1794
    return mObjectScreenRatio*mInitScreenRatio;
1795
    }
1796

    
1797
///////////////////////////////////////////////////////////////////////////////////////////////////
1798

    
1799
  boolean isSolved()
1800
    {
1801
    if( mSolvedFunctionIndex==0 ) return isSolved0();
1802
    if( mSolvedFunctionIndex==1 ) return isSolved1();
1803

    
1804
    return false;
1805
    }
1806

    
1807
///////////////////////////////////////////////////////////////////////////////////////////////////
1808

    
1809
  int computeNearestAngle(int basicAngle, float angle, float speed)
1810
    {
1811
    int nearestAngle = 360/basicAngle;
1812
    int tmp = (int)((angle+nearestAngle/2)/nearestAngle);
1813
    if( angle< -(nearestAngle*0.5) ) tmp-=1;
1814

    
1815
    if( tmp!=0 ) return nearestAngle*tmp;
1816

    
1817
    return speed> 1.2f ? nearestAngle*(angle>0 ? 1:-1) : 0;
1818
    }
1819

    
1820
///////////////////////////////////////////////////////////////////////////////////////////////////
1821
// INTERNAL API - those are called from 'effects' package
1822
///////////////////////////////////////////////////////////////////////////////////////////////////
1823

    
1824
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
1825
    {
1826
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total, getSignature() );
1827
    }
1828

    
1829
///////////////////////////////////////////////////////////////////////////////////////////////////
1830

    
1831
  public Static4D getRotationQuat()
1832
    {
1833
    return mQuat;
1834
    }
1835

    
1836
///////////////////////////////////////////////////////////////////////////////////////////////////
1837

    
1838
  public float getSize()
1839
    {
1840
    return mSize;
1841
    }
1842

    
1843
///////////////////////////////////////////////////////////////////////////////////////////////////
1844

    
1845
  public void apply(Effect effect, int position)
1846
    {
1847
    mEffects.apply(effect, position);
1848
    }
1849

    
1850
///////////////////////////////////////////////////////////////////////////////////////////////////
1851

    
1852
  public void remove(long effectID)
1853
    {
1854
    mEffects.abortById(effectID);
1855
    }
1856

    
1857
///////////////////////////////////////////////////////////////////////////////////////////////////
1858

    
1859
  public MeshBase getObjectMesh()
1860
    {
1861
    return mMesh;
1862
    }
1863

    
1864
///////////////////////////////////////////////////////////////////////////////////////////////////
1865

    
1866
  public DistortedEffects getObjectEffects()
1867
    {
1868
    return mEffects;
1869
    }
1870

    
1871
///////////////////////////////////////////////////////////////////////////////////////////////////
1872

    
1873
  public int getCubitType(int cubit)
1874
    {
1875
    return mCubits[cubit].getType();
1876
    }
1877

    
1878
///////////////////////////////////////////////////////////////////////////////////////////////////
1879

    
1880
  public float[] getCubitOffset(int cubit)
1881
    {
1882
    return mCubits[cubit].getOffset();
1883
    }
1884

    
1885
///////////////////////////////////////////////////////////////////////////////////////////////////
1886
// PUBLIC API
1887
///////////////////////////////////////////////////////////////////////////////////////////////////
1888

    
1889
  public int getCubitFaceColorIndex(int cubit, int face)
1890
    {
1891
    Static4D texMap = mMesh.getTextureMap(mNumFaceColors *cubit + face);
1892

    
1893
    int x = (int)(texMap.get0()/texMap.get2());
1894
    int y = (int)(texMap.get1()/texMap.get3());
1895

    
1896
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
1897
    }
1898

    
1899
///////////////////////////////////////////////////////////////////////////////////////////////////
1900

    
1901
  public int[] getNumLayers()
1902
    {
1903
    return mNumLayers;
1904
    }
1905

    
1906
///////////////////////////////////////////////////////////////////////////////////////////////////
1907

    
1908
  public synchronized void solve()
1909
    {
1910
    for(int i=0; i<mNumCubits; i++)
1911
      {
1912
      mCubits[i].solve();
1913
      }
1914

    
1915
    recomputeFaceOffsets();
1916

    
1917
    for(int i=0; i<mNumCubits; i++)
1918
      {
1919
      mCubits[i].computeRotationRow();
1920
      setCubitQuat(i,mCubits[i].computeAssociation(),0);
1921
      }
1922
    }
1923

    
1924
///////////////////////////////////////////////////////////////////////////////////////////////////
1925

    
1926
  public int getCubitQuatIndex(int cubit)
1927
    {
1928
    return (cubit>=0 && cubit<mNumCubits) ? mCubits[cubit].mQuatIndex : 0;
1929
    }
1930

    
1931
///////////////////////////////////////////////////////////////////////////////////////////////////
1932

    
1933
  public int getCubitRotRow(int cubit, int axis)
1934
    {
1935
    return mCubits[cubit].getRotRow(axis);
1936
    }
1937

    
1938
///////////////////////////////////////////////////////////////////////////////////////////////////
1939

    
1940
  public Bitmap getStickerBitmap()
1941
    {
1942
    return mBitmap;
1943
    }
1944

    
1945
///////////////////////////////////////////////////////////////////////////////////////////////////
1946

    
1947
  public DistortedNode getNode()
1948
    {
1949
    return mNode;
1950
    }
1951

    
1952
///////////////////////////////////////////////////////////////////////////////////////////////////
1953

    
1954
  public int getNumStickerTypes()
1955
    {
1956
    return mNumStickerTypes;
1957
    }
1958

    
1959
///////////////////////////////////////////////////////////////////////////////////////////////////
1960
// this is here only so it can be overridden in TwistyJSON soo that we can get this from JSON.
1961

    
1962
  public int getNumCubitFaces()
1963
    {
1964
    return 0;
1965
    }
1966

    
1967
///////////////////////////////////////////////////////////////////////////////////////////////////
1968

    
1969
  public TouchControl getTouchControl()
1970
    {
1971
    if( mTouchControl==null )
1972
      {
1973
      switch(getTouchControlType())
1974
        {
1975
        case TC_TETRAHEDRON      : mTouchControl = new TouchControlTetrahedron(this);
1976
                                   break;
1977
        case TC_HEXAHEDRON       : mTouchControl = new TouchControlHexahedron(this);
1978
                                   break;
1979
        case TC_OCTAHEDRON       : mTouchControl = new TouchControlOctahedron(this);
1980
                                   break;
1981
        case TC_DODECAHEDRON     : mTouchControl = new TouchControlDodecahedron(this);
1982
                                   break;
1983
        case TC_CUBOID           : int[] numLayers = getNumLayers();
1984
                                   mTouchControl = new TouchControlCuboids(this,getDist3D(numLayers));
1985
                                   break;
1986
        case TC_CHANGING_MIRROR  : mTouchControl = new TouchControlMirror(this);
1987
                                   break;
1988
        case TC_CHANGING_SQUARE  : mTouchControl = new TouchControlSquare(this);
1989
                                   break;
1990
        case TC_CHANGING_SHAPEMOD: mTouchControl = new TouchControlShapemod(this);
1991
                                   break;
1992
        }
1993
      }
1994
    return mTouchControl;
1995
    }
1996

    
1997
///////////////////////////////////////////////////////////////////////////////////////////////////
1998

    
1999
  protected void setReader(JsonReader reader)
2000
    {
2001
    // empty
2002
    }
2003

    
2004
///////////////////////////////////////////////////////////////////////////////////////////////////
2005
  // for JSON only
2006
  public abstract int getTouchControlType();
2007
  public abstract int getTouchControlSplit();
2008
  public abstract boolean[][] getLayerRotatable(int[] numLayers);
2009
  public abstract int[][][] getEnabled();
2010
  public abstract float[] getDist3D(int[] numLayers);
2011
  public abstract Static3D[] getFaceAxis();
2012
  public abstract ScrambleState[] getScrambleStates();
2013
  public abstract float[][] getCuts(int[] numLayers);
2014
  public abstract float getStickerRadius();
2015
  public abstract float getStickerStroke();
2016
  public abstract float[][] getStickerAngles();
2017
  public abstract int getCubitVariant(int cubit, int[] numLayers);
2018
  public abstract ObjectShape getObjectShape(int variant);
2019
  public abstract ObjectFaceShape getObjectFaceShape(int variant);
2020
  public abstract int getNumCubitVariants(int[] numLayers);
2021
  public abstract float[][] getCubitPositions(int[] numLayers);
2022
  public abstract Static4D getCubitQuats(int cubit, int[] numLayers);
2023
  public abstract int getNumFaceColors();
2024
  public abstract float getScreenRatio();
2025
  public abstract int getColor(int face);
2026
  public abstract String getShortName();
2027
  public abstract ObjectSignature getSignature();
2028

    
2029
  // not only for JSON
2030
  public abstract Static3D[] getRotationAxis();
2031
  public abstract int[][] getBasicAngles();
2032
  public abstract int getNumFaces();
2033
  public abstract String getObjectName();
2034
  public abstract String getInventor();
2035
  public abstract int getYearOfInvention();
2036
  public abstract int getComplexity();
2037
  public abstract int getFOV();
2038
  public abstract String[][] getTutorials();
2039
  }
(12-12/13)