Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyObject.java @ 22d72239

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.graphics.Bitmap;
18
import android.graphics.Canvas;
19
import android.graphics.Paint;
20

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

    
40
import org.distorted.objectlib.helpers.FactoryCubit;
41
import org.distorted.objectlib.helpers.FactorySticker;
42
import org.distorted.objectlib.helpers.ObjectFaceShape;
43
import org.distorted.objectlib.helpers.ObjectLibInterface;
44
import org.distorted.objectlib.helpers.ObjectShape;
45
import org.distorted.objectlib.metadata.Metadata;
46
import org.distorted.objectlib.signature.ObjectSignature;
47
import org.distorted.objectlib.helpers.ObjectSticker;
48
import org.distorted.objectlib.helpers.ObjectStickerOverride;
49
import org.distorted.objectlib.helpers.ObjectVertexEffects;
50
import org.distorted.objectlib.helpers.OperatingSystemInterface;
51
import org.distorted.objectlib.helpers.QuatGroupGenerator;
52
import org.distorted.objectlib.scrambling.ObjectScrambler;
53
import org.distorted.objectlib.json.JsonReader;
54
import org.distorted.objectlib.scrambling.ScrambleEdgeGenerator;
55
import org.distorted.objectlib.tablebases.ImplementedTablebasesList;
56
import org.distorted.objectlib.tablebases.TablebasesAbstract;
57
import org.distorted.objectlib.touchcontrol.*;
58

    
59
import static org.distorted.objectlib.touchcontrol.TouchControl.*;
60

    
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62

    
63
public abstract class TwistyObject
64
  {
65
  public static final int MODE_ICON = 0;
66
  public static final int MODE_NORM = 1;
67

    
68
  public static final int COLOR_STROKE   = 0xff000000;
69
  public static final int COLOR_INTERNAL = 0xff000000;
70

    
71
  private static final int DEFAULT_TEXTURE_HEIGHT = 256;
72
  private static final int DEFAULT_TEXTURE_ROWS   = 8;
73

    
74
  public static final float SQ2 = (float)Math.sqrt(2);
75
  public static final float SQ3 = (float)Math.sqrt(3);
76
  public static final float SQ5 = (float)Math.sqrt(5);
77
  public static final float SQ6 = (float)Math.sqrt(6);
78

    
79
  private static final float MAX_SIZE_CHANGE = 1.35f;
80
  private static final float MIN_SIZE_CHANGE = 0.75f;
81

    
82
  private static final Static3D CENTER = new Static3D(0,0,0);
83

    
84
  protected float[][][][] mStickerCoords;
85
  protected Static4D[] mObjectQuats;
86

    
87
  private int[][] mStickerVariants;
88
  private float[] mStickerScales;
89
  private TwistyObjectCubit[] mCubits;
90
  private TwistyObjectSolved mSolved;
91
  private MeshBase[] mMeshes;
92
  private int mNumCubits, mNumQuats, mNumFaceColors, mNumTextures, mNumOverrides;
93
  private int mNumCubitFaces, mNumStickerTypes;
94
  private Static3D[] mAxis;
95
  private float[][] mCuts;
96
  private int[][] mMinimalCubiesInRow;
97
  private int[] mNumCuts;
98
  private float[][] mOrigPos;
99
  private Static4D[] mOrigQuat;
100
  private Static4D[] mMixupModeQuats;
101
  private boolean mIsInMixupMode;
102
  private Static4D mQuat;
103
  private int[] mNumLayers;
104
  private float mSize;
105
  private DistortedEffects mEffects;
106
  private Static3D mObjectScale;
107
  private int[] mQuatDebug;
108
  private DistortedTexture mTexture;
109
  private float mInitScreenRatio;
110
  private boolean mIsBandaged;
111
  private float mObjectScreenRatio;
112
  private int mNumTexRows, mNumTexCols, mTexHeight;
113
  private MeshBase mMesh;
114
  private ObjectScrambler mScrambler;
115
  private TouchControl mTouchControl;
116
  private DistortedNode mNode;
117
  private ObjectLibInterface mInterface;
118
  private Bitmap mBitmap;
119
  private ObjectSticker[] mStickers;
120
  private ObjectShape[] mShapes;
121
  private int mNumCubitVariants;
122
  private int[][] mCubitFaceColors;
123
  private int[][] mVariantFaceIsOuter;
124
  private int[][] mBasicAngles;
125
  private int mIconMode;
126
  private Metadata mMetadata;
127
  private float[][] mRowOffsets;
128
  private boolean[] mBelongs;
129
  private float[] mTmp;
130
  private int mNumPuzzleFaces;
131
  private ObjectStickerOverride[] mStickerOverrides;
132
  private boolean mError;
133
  private String mErrorString;
134
  private int mMaxNumLayers;
135
  private int mNumAxis;
136
  private boolean mThereAreDeciders;
137
  private TwistyLayerRotations mRotation;
138
  private int[] mColorTable;
139
  private int[] mOriginalColorTable;
140
  private float mTextureBorderMultiplier, mTextureCornerMultiplier;
141
  private boolean mCurrentColorSchemeSubmittable;
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
  TwistyObject(int iconMode, Static4D quat, Static3D move, float scale, InitAssets asset)
146
    {
147
    try
148
      {
149
      InputStream jsonStream = asset!=null ? asset.getJsonStream(): null;
150
      JsonReader reader = new JsonReader();
151
      reader.parseJsonFile(jsonStream);
152
      setReader(reader);
153
      mNumLayers = reader.getNumLayers();
154
      mSize      = reader.getSize();
155
      mMetadata  = null;
156
      initialize(iconMode,quat,move,scale,asset,true);
157
      if( asset!=null ) asset.close();
158
      mError = false;
159
      mErrorString=null;
160
      }
161
    catch(Exception ex)
162
      {
163
      mError = true;
164
      mErrorString = ex.getMessage();
165
      }
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  public TwistyObject(int iconMode, float size, Static4D quat, Static3D move, float scale, Metadata meta, InitAssets asset)
171
    {
172
    mNumLayers = meta.getNumLayers();
173
    mSize      = size;
174
    mMetadata  = meta;
175
    initialize(iconMode,quat,move,scale,asset,false);
176
    if( asset!=null ) asset.close();
177
    mError = false;
178
    mErrorString = null;
179
    }
180

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

    
183
  private void debugQuat(Static4D quat, int cubitIndex, float axisX, float axisY, float axisZ, float angle, int place)
184
    {
185
    float[] tracking = mCubits[cubitIndex].getTrackingPoint();
186

    
187
    if( tracking!=null )
188
      {
189
      String problem = (getShortName()+" "+cubitIndex+" "+quat.get0()+" "+quat.get1()+" "+quat.get2()+" "+quat.get3());
190
      problem += (" "+angle+" "+place+" "+tracking[0]+" "+tracking[1]+" "+tracking[2]);
191
      problem += (" "+axisX+" "+axisY+" "+axisZ);
192

    
193
      mInterface.reportProblem(problem, true);
194
      }
195
    }
196

    
197
///////////////////////////////////////////////////////////////////////////////////////////////////
198

    
199
  private void initialize(int iconMode, Static4D quat, Static3D move, float scale, InitAssets asset, boolean fromJSON)
200
    {
201
    mIconMode = iconMode;
202
    mQuat = quat;
203
    mAxis = getRotationAxis();
204
    mInitScreenRatio = getScreenRatio();
205
    mBasicAngles = getBasicAngles();
206
    mObjectQuats = getQuats();
207
    mNumQuats = mObjectQuats.length;
208
    mOrigPos = getCubitPositions(mNumLayers);
209
    mNumPuzzleFaces = getNumPuzzleFaces();
210
    mRowOffsets = new float[mNumPuzzleFaces][3];
211
    mTmp = new float[4];
212
    mNumAxis = mAxis.length;
213
    mCuts = getCuts(mNumLayers);
214
    mNumCuts = new int[mNumAxis];
215
    mMaxNumLayers = -1;
216
    for(int i=0; i<mNumAxis; i++)
217
      {
218
      if( mMaxNumLayers<mNumLayers[i] ) mMaxNumLayers = mNumLayers[i];
219
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
220
      }
221

    
222
    mOriginalColorTable = getColorTable();
223
    mNumFaceColors = mOriginalColorTable.length;
224
    mColorTable = new int[mNumFaceColors];
225
    recreateFaceColors();
226
    mTextureBorderMultiplier = 1.0f;
227
    mTextureCornerMultiplier = 1.0f;
228

    
229
    mMinimalCubiesInRow = getMinimalCubiesInRow();
230
    mNumCubits = mOrigPos.length;
231

    
232
    mBelongs = new boolean[mNumCubits];
233

    
234
    int scramblingType = getScrambleType();
235
    int[][] edges = getScrambleEdges();
236
    int[][] algorithms = getScrambleAlgorithms();
237

    
238
    OperatingSystemInterface os = asset==null ? null : asset.getOS();
239
    TablebasesAbstract tablebase = os!=null ? getTablebase(os) : null;
240
    mScrambler = new ObjectScrambler(scramblingType,mNumAxis,mNumLayers,algorithms,edges,tablebase);
241

    
242
    boolean bandaged=false;
243

    
244
    for( int c=0; c<mNumCubits; c++)
245
      {
246
      if( mOrigPos[c].length>3 )
247
        {
248
        bandaged=true;
249
        break;
250
        }
251
      }
252
    mIsBandaged = bandaged;
253
    mQuatDebug = new int[mNumCubits];
254

    
255
    mObjectScale = new Static3D(scale,scale,scale);
256
    setObjectRatioNow(scale,720);
257

    
258
    mEffects = new DistortedEffects();
259
    createQuaternionEffects();
260

    
261
    mRotation = new TwistyLayerRotations(this,mAxis,mNumLayers,getGhostAngle(),mEffects);
262

    
263
    MatrixEffectScale scaleEffect = new MatrixEffectScale(mObjectScale);
264
    MatrixEffectQuaternion quatEffect = new MatrixEffectQuaternion(mQuat, CENTER);
265
    MatrixEffectMove moveEffect = new MatrixEffectMove(move);
266

    
267
    InputStream meshStream = asset!=null ? asset.getMeshStream(): null;
268
    boolean fromDMESH = (meshStream!=null);
269
    getQuatsAndShapes(fromDMESH,fromJSON);
270
    createMeshAndCubits(meshStream,fromDMESH);
271
    setUpTextures(fromDMESH,fromJSON);
272

    
273
    int index = getSolvedFunctionIndex();
274
    mSolved = new TwistyObjectSolved(this,mOrigPos,index);
275
    mSolved.setupSolvedQuats(getSolvedQuats());
276
    mSolved.setPuzzleFaceColor(mColorTable);
277

    
278
    mEffects.apply(quatEffect);
279
    mEffects.apply(scaleEffect);
280
    mEffects.apply(moveEffect);
281

    
282
    mNode = new DistortedNode(mTexture,mEffects,mMesh);
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

    
287
  public void recreateFaceColors()
288
    {
289
    for(int i=0; i<mNumFaceColors; i++) mColorTable[i] = mOriginalColorTable[i];
290
    mCurrentColorSchemeSubmittable = true;
291
    }
292

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294
// in degrees so that everything can be treated modulo 360
295

    
296
  public int getGhostAngle()
297
    {
298
    return 0;
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  private TablebasesAbstract getTablebase(OperatingSystemInterface os)
304
    {
305
    String shortName = getShortName();
306
    return ImplementedTablebasesList.createPacked(os,shortName);
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
  private void createQuaternionEffects()
312
    {
313
    if( mNumQuats<=ObjectControl.MAX_QUATS )
314
      {
315
      mIsInMixupMode = false;
316

    
317
      for( int q=0; q<mNumQuats; q++)
318
        {
319
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mObjectQuats[q],CENTER);
320
        vq.setMeshAssociation(0,q);
321
        mEffects.apply(vq);
322
        }
323
      }
324
    else if( mNumCubits<=ObjectControl.MAX_QUATS )
325
      {
326
      mIsInMixupMode = true;
327
      mMixupModeQuats = new Static4D[mNumCubits];
328

    
329
      for( int q=0; q<mNumCubits; q++)
330
        {
331
        mMixupModeQuats[q] = new Static4D(mObjectQuats[0]);
332
        VertexEffectQuaternion vq = new VertexEffectQuaternion(mMixupModeQuats[q],CENTER);
333
        vq.setMeshAssociation(0,q);
334
        mEffects.apply(vq);
335
        }
336
      }
337
    else
338
      {
339
      android.util.Log.e("D", "object has too many quaternions ("+mNumQuats+") or too many cubits ("+mNumCubits+")");
340
      }
341
    }
342

    
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344

    
345
  private Static3D getPos(float[] origPos)
346
    {
347
    int len = origPos.length/3;
348
    float sumX = 0.0f;
349
    float sumY = 0.0f;
350
    float sumZ = 0.0f;
351

    
352
    for(int i=0; i<len; i++)
353
      {
354
      sumX += origPos[3*i  ];
355
      sumY += origPos[3*i+1];
356
      sumZ += origPos[3*i+2];
357
      }
358

    
359
    sumX /= len;
360
    sumY /= len;
361
    sumZ /= len;
362

    
363
    return new Static3D(sumX,sumY,sumZ);
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

    
368
  private void createOuterFaces()
369
    {
370
    for(int v=0; v<mNumCubitVariants; v++)
371
      {
372
      int numFaces = mShapes[v].getNumFaces();
373
      mVariantFaceIsOuter[v] = new int[numFaces];
374
      }
375

    
376
    for( int cubit=0; cubit<mNumCubits; cubit++)
377
      {
378
      int variant = getCubitVariant(cubit,mNumLayers);
379
      int numFaces = mShapes[variant].getNumFaces();
380

    
381
      for(int face=0; face<numFaces; face++)
382
        if( getCubitFaceColor(cubit,face)>=0 )
383
          {
384
          mVariantFaceIsOuter[variant][face] = 1;
385
          }
386
      }
387
    }
388

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

    
391
  private void getQuatsAndShapes(boolean fromDMESH, boolean fromJSON)
392
    {
393
    mNumCubitVariants = getNumCubitVariants(mNumLayers);
394

    
395
    if( !fromDMESH || !fromJSON )
396
      {
397
      FactoryCubit factory = FactoryCubit.getInstance();
398
      factory.clear();
399

    
400
      mShapes = new ObjectShape[mNumCubitVariants];
401
      for(int v=0; v<mNumCubitVariants; v++) mShapes[v] = getObjectShape(v);
402
      mNumCubitFaces = ObjectShape.computeNumComponents(mShapes);
403
      mVariantFaceIsOuter = new int[mNumCubitVariants][];
404

    
405
      mOrigQuat = new Static4D[mNumCubits];
406
      for(int i=0; i<mNumCubits; i++) mOrigQuat[i] = getCubitQuats(i,mNumLayers);
407

    
408
      if( !fromJSON )
409
        {
410
        mCubitFaceColors = ObjectShape.computeColors(mShapes,mOrigPos,mOrigQuat,this);
411
        createOuterFaces();
412
        }
413

    
414
      if( fromDMESH )
415
        {
416
        for(int i=0; i<mNumCubitVariants; i++) factory.createNewFaceTransform(mShapes[i], mVariantFaceIsOuter[i]);
417
        }
418
      }
419
    }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422

    
423
  private void createMeshAndCubits(InputStream stream, boolean fromDMESH)
424
    {
425
    mCubits = new TwistyObjectCubit[mNumCubits];
426

    
427
    if( fromDMESH )
428
      {
429
      DataInputStream dos = new DataInputStream(stream);
430
      mMesh = new MeshFile(dos);
431

    
432
      try
433
        {
434
        stream.close();
435
        }
436
      catch(IOException e)
437
        {
438
        android.util.Log.e("meshFile", "Error closing InputStream: "+e);
439
        }
440
      }
441
    else
442
      {
443
      MeshBase[] cubitMesh = new MeshBase[mNumCubits];
444

    
445
      for(int i=0; i<mNumCubits; i++)
446
        {
447
        cubitMesh[i] = createCubitMesh(i,mNumLayers,mNumCubitFaces);
448
        Static3D pos = getPos(mOrigPos[i]);
449
        cubitMesh[i].apply(new MatrixEffectMove(pos),1,0);
450
        }
451

    
452
      mMesh = new MeshJoined(cubitMesh);
453

    
454
      float pillowCoeff = getPillowCoeff();
455

    
456
      if( pillowCoeff!=1.0f )
457
        {
458
        float radius = getCircumscribedRadius();
459
        Static1D coeff = new Static1D(pillowCoeff);
460
        Static4D region= new Static4D(0,0,0,radius);
461
        VertexEffectSink sink = new VertexEffectSink(coeff,CENTER,region);
462
        mMesh.apply(sink);
463
        }
464
      }
465

    
466
    for(int i=0; i<mNumCubits; i++)
467
      {
468
      mCubits[i] = new TwistyObjectCubit(this,mOrigPos[i],mNumAxis,mMaxNumLayers,i);
469
      setCubitQuat(i,0);
470

    
471
      if( !mThereAreDeciders && mCubits[i].getType()==TwistyObjectCubit.TYPE_DECIDER )
472
        {
473
        mThereAreDeciders = true;
474
        }
475
      }
476
    }
477

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

    
480
  private MeshBase createCubitMesh(int cubit, int[] numLayers, int numComponents)
481
    {
482
    int variant = getCubitVariant(cubit,numLayers);
483

    
484
    if( mMeshes==null ) mMeshes = new MeshBase[mNumCubitVariants];
485

    
486
    if( mMeshes[variant]==null )
487
      {
488
      ObjectFaceShape faceShape = getObjectFaceShape(variant);
489
      ObjectVertexEffects effects = getVertexEffects(variant);
490
      FactoryCubit factory = FactoryCubit.getInstance();
491
      factory.createNewFaceTransform(mShapes[variant],mVariantFaceIsOuter[variant]);
492
      mMeshes[variant] = factory.createRoundedSolid(mShapes[variant],faceShape,effects,numComponents);
493
      }
494

    
495
    MeshBase mesh = mMeshes[variant].copy(true);
496
    MatrixEffectQuaternion quat = new MatrixEffectQuaternion( mOrigQuat[cubit], CENTER );
497
    mesh.apply(quat,0xffffffff,0);
498

    
499
    return mesh;
500
    }
501

    
502
///////////////////////////////////////////////////////////////////////////////////////////////////
503

    
504
  private void figureOutBitmapDimensions(int numTextures)
505
    {
506
    int maxSize = DistortedLibrary.getMaxTextureSize();
507

    
508
    mTexHeight  = DEFAULT_TEXTURE_HEIGHT;
509

    
510
    while(true)
511
      {
512
      mNumTexCols = DEFAULT_TEXTURE_ROWS;
513
      mNumTexRows = numTextures/mNumTexCols + 1;
514

    
515
      if( mNumTexRows*mTexHeight <= maxSize &&
516
          mNumTexCols*mTexHeight <= maxSize  ) break;
517

    
518
      mTexHeight/=2;
519
      }
520
    }
521

    
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523

    
524
  private void setUpTextures(boolean fromDMESH, boolean fromJSON)
525
    {
526
    mTexture = new DistortedTexture();
527

    
528
    if( fromJSON )
529
      {
530
      mNumStickerTypes = getNumStickerTypes();
531
      mNumCubitFaces = getNumCubitFaces();
532
      }
533
    else
534
      {
535
      FactoryCubit factory = FactoryCubit.getInstance();
536
      mStickerCoords   = factory.getStickerCoords();
537
      mStickerVariants = factory.getStickerVariants();
538
      mStickerScales   = factory.getStickerScales();
539
      adjustStickerCoords();
540
      mNumStickerTypes = (mStickerCoords==null ? 0 : mStickerCoords.length);
541
      }
542

    
543
    mStickerOverrides = getStickerOverrides();
544
    mNumOverrides = mStickerOverrides==null ? 0 : mStickerOverrides.length;
545

    
546
    mNumTextures= mNumFaceColors*mNumStickerTypes + mNumOverrides;
547
    figureOutBitmapDimensions(mNumTextures);
548
    if( mNumTexCols*mNumTexRows < mNumTextures+1 ) mNumTexRows++;
549

    
550
    if( !fromDMESH || shouldResetTextureMaps() ) resetAllTextureMaps();
551
    else overrideCubitFaceColor();
552

    
553
    setTexture();
554
    }
555

    
556
///////////////////////////////////////////////////////////////////////////////////////////////////
557

    
558
  public Metadata getMetadata()
559
    {
560
    return mMetadata;
561
    }
562

    
563
///////////////////////////////////////////////////////////////////////////////////////////////////
564

    
565
  public boolean isInIconMode()
566
    {
567
    return mIconMode==MODE_ICON;
568
    }
569

    
570
///////////////////////////////////////////////////////////////////////////////////////////////////
571

    
572
  public int getVariantStickerShape(int variant, int face)
573
    {
574
    if( variant <mStickerVariants.length )
575
      {
576
      int[] var = mStickerVariants[variant];
577
      return face>=var.length ? -1 : var[face];
578
      }
579
    return -1;
580
    }
581

    
582
///////////////////////////////////////////////////////////////////////////////////////////////////
583

    
584
  public boolean shouldResetTextureMaps()
585
    {
586
    return false;
587
    }
588

    
589
///////////////////////////////////////////////////////////////////////////////////////////////////
590

    
591
  public int[][] getScrambleAlgorithms()
592
    {
593
    return ScrambleEdgeGenerator.getScramblingAlgorithms(mBasicAngles);
594
    }
595

    
596
///////////////////////////////////////////////////////////////////////////////////////////////////
597

    
598
  private boolean sticksOut(Static3D[] faceAxis, float[] dist, float x, float y, float z )
599
    {
600
    final float MAXERR = 0.05f;
601
    int numAxis = dist.length;
602

    
603
    for(int i=0; i<numAxis; i++)
604
      {
605
      Static3D ax = faceAxis[i];
606
      float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
607
      if( len>mSize*dist[i]+MAXERR ) return true;
608
      }
609

    
610
    return false;
611
    }
612

    
613
///////////////////////////////////////////////////////////////////////////////////////////////////
614

    
615
  private boolean doesNotStickOut(int variant, float px, float py, float pz, float[] tmp, Static4D quat)
616
    {
617
    float[][] vertices = mShapes[variant].getVertices();
618
    Static3D[] axis = getFaceAxis();
619
    float[] dist3D = getDist3D(mNumLayers);
620

    
621
    for( float[] vertex : vertices)
622
      {
623
      float x = vertex[0];
624
      float y = vertex[1];
625
      float z = vertex[2];
626

    
627
      QuatHelper.rotateVectorByQuat(tmp, x, y, z, 1, quat);
628

    
629
      float mx = tmp[0] + px;
630
      float my = tmp[1] + py;
631
      float mz = tmp[2] + pz;
632

    
633
      if( sticksOut(axis, dist3D, mx,my,mz) ) return false;
634
      }
635

    
636
    return true;
637
    }
638

    
639
///////////////////////////////////////////////////////////////////////////////////////////////////
640

    
641
  private float computeAvg(float[] pos, int offset)
642
    {
643
    int len = pos.length/3;
644
    float ret=0.0f;
645
    for(int i=0; i<len; i++) ret += pos[3*i+offset];
646
    ret /= len;
647

    
648
    return ret;
649
    }
650

    
651
///////////////////////////////////////////////////////////////////////////////////////////////////
652

    
653
  protected void displayCubitQuats()
654
    {
655
    StringBuilder builder = new StringBuilder();
656
    float[] tmp = new float[4];
657
    float ERR = 0.01f;
658

    
659
    for(int cubit=0; cubit<mNumCubits; cubit++)
660
      {
661
      builder.append(cubit);
662
      builder.append(" : ");
663

    
664
      int refCubit,variant = getCubitVariant(cubit,mNumLayers);
665

    
666
      for(refCubit=0; refCubit<mNumCubits; refCubit++)
667
        {
668
        if( getCubitVariant(refCubit,mNumLayers)==variant ) break;
669
        }
670

    
671
      float[] curpos = mOrigPos[cubit];
672
      float[] refpos = mOrigPos[refCubit];
673
      float refX = computeAvg(refpos,0);
674
      float refY = computeAvg(refpos,1);
675
      float refZ = computeAvg(refpos,2);
676
      float curX = computeAvg(curpos,0);
677
      float curY = computeAvg(curpos,1);
678
      float curZ = computeAvg(curpos,2);
679

    
680
      for(int quat=0; quat<mNumQuats; quat++)
681
        {
682
        QuatHelper.rotateVectorByQuat(tmp,refX,refY,refZ,0,mObjectQuats[quat]);
683

    
684
        float dx = tmp[0]-curX;
685
        float dy = tmp[1]-curY;
686
        float dz = tmp[2]-curZ;
687

    
688
        if( dx>-ERR && dx<ERR && dy>-ERR && dy<ERR && dz>-ERR && dz<ERR )
689
          {
690
          if( doesNotStickOut(variant,curX,curY,curZ,tmp,mObjectQuats[quat]) )
691
            {
692
            builder.append(quat);
693
            builder.append(',');
694
            }
695
          else
696
            {
697
            android.util.Log.e("D", "cubit: "+cubit+" quat: "+quat+" : center correct, but shape sticks out");
698
            }
699
          }
700
        }
701

    
702
      builder.append('\n');
703
      }
704

    
705
    android.util.Log.e("D", "cubitQuats: \n"+builder );
706
    }
707

    
708
///////////////////////////////////////////////////////////////////////////////////////////////////
709

    
710
  public int getCubitRotationType(int cubit)
711
    {
712
    return TwistyObjectCubit.TYPE_NORMAL;
713
    }
714

    
715
///////////////////////////////////////////////////////////////////////////////////////////////////
716

    
717
  float[] getTrackingPoint(int cubitIndex, int cubitType)
718
    {
719
    if( cubitType!=TwistyObjectCubit.TYPE_NORMAL )
720
      {
721
      int variant = getCubitVariant(cubitIndex,mNumLayers);
722

    
723
      // object must have been created from JSON
724
      if( mVariantFaceIsOuter==null || mVariantFaceIsOuter[variant]==null )
725
        {
726
        mVariantFaceIsOuter = getVariantFaceIsOuter();
727
        }
728
      if( mShapes==null )
729
        {
730
        mShapes = new ObjectShape[mNumCubitVariants];
731
        }
732
      if( mShapes[variant]==null )
733
        {
734
        mShapes[variant] = getObjectShape(variant);
735
        }
736
      if( mOrigQuat==null )
737
        {
738
        mOrigQuat = new Static4D[mNumCubits];
739
        }
740
      if( mOrigQuat[cubitIndex]==null )
741
        {
742
        mOrigQuat[cubitIndex] = getCubitQuats(cubitIndex,mNumLayers);
743
        }
744

    
745
      int outer=-1, faces = mShapes[variant].getNumFaces();
746

    
747
      for(int i=0; i<faces; i++)
748
        {
749
        if( mVariantFaceIsOuter[variant][i]==1 )
750
          {
751
          outer=i;
752
          break;
753
          }
754
        }
755

    
756
      if( outer>=0 )
757
        {
758
        float[] v = mShapes[variant].getFirstVertexInFace(outer);
759
        float[] ret = new float[3];
760
        float[] curpos = mOrigPos[cubitIndex];
761
        Static4D quat = mOrigQuat[cubitIndex];
762
        QuatHelper.rotateVectorByQuat(mTmp, v[0], v[1], v[2], 1, quat);
763

    
764
        ret[0] = mTmp[0]+computeAvg(curpos,0);
765
        ret[1] = mTmp[1]+computeAvg(curpos,1);
766
        ret[2] = mTmp[2]+computeAvg(curpos,2);
767

    
768
        return ret;
769
        }
770
      else
771
        {
772
        android.util.Log.e("D", "Error in getTrackingPoint: no outer face??");
773
        }
774
      }
775

    
776
    return null;
777
    }
778

    
779
///////////////////////////////////////////////////////////////////////////////////////////////////
780

    
781
  public int computeCurrentPuzzleFace(int type, float[] vertex)
782
    {
783
    if( type!=TwistyObjectCubit.TYPE_NORMAL )
784
      {
785
      Static3D[] axis = getFaceAxis();
786
      float[] dist3D = getDist3D(mNumLayers);
787
      final float MAXERR = 0.98f;
788
      int numAxis = axis.length;
789
      float x = vertex[0];
790
      float y = vertex[1];
791
      float z = vertex[2];
792

    
793
      for(int i=0; i<numAxis; i++)
794
        {
795
        Static3D ax = axis[i];
796
        float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
797
        if( len>mSize*dist3D[i]*MAXERR ) return i;
798
        }
799

    
800
      return -2;
801
      }
802

    
803
    return -1;
804
    }
805

    
806
///////////////////////////////////////////////////////////////////////////////////////////////////
807

    
808
  public float[] getCubitRowOffset(int cubitIndex)
809
    {
810
    return null;
811
    }
812

    
813
///////////////////////////////////////////////////////////////////////////////////////////////////
814

    
815
  void setRotationRowOffset(int puzzleFace, float[] offset)
816
    {
817
    mRowOffsets[puzzleFace][0] = offset[0];
818
    mRowOffsets[puzzleFace][1] = offset[1];
819
    mRowOffsets[puzzleFace][2] = offset[2];
820
    }
821

    
822
///////////////////////////////////////////////////////////////////////////////////////////////////
823

    
824
  int getNumAxis()
825
    {
826
    return mNumAxis;
827
    }
828

    
829
///////////////////////////////////////////////////////////////////////////////////////////////////
830

    
831
  public int[][] getSolvedQuats()
832
    {
833
    return null;
834
    }
835

    
836
///////////////////////////////////////////////////////////////////////////////////////////////////
837

    
838
  protected int[][] getOldSolvedQuats()
839
    {
840
    return mSolved.getSolvedQuats(mNumLayers,mNumCubitFaces,mCubitFaceColors);
841
    }
842

    
843
///////////////////////////////////////////////////////////////////////////////////////////////////
844
// 0: old, groups-of-quaternions based function.
845
// Still used by: Masterball, Mirror Sqewb, Mirror Jing, Mirror Pyraminx & the Penrose Cubes.
846
//
847
// 1: specific function only for the Dino4.
848
//
849
// 2: the new default; automatic way based on automatic detection of monochromatic puzzle surfaces.
850

    
851
  public int getSolvedFunctionIndex()
852
    {
853
    return 2;
854
    }
855

    
856
///////////////////////////////////////////////////////////////////////////////////////////////////
857

    
858
  int computeRow(float[] pos, int axisIndex, int cubitType, int puzzleFace)
859
    {
860
    int ret=0;
861
    int len = pos.length / 3;
862
    Static3D axis = mAxis[axisIndex];
863
    float axisX = axis.get0();
864
    float axisY = axis.get1();
865
    float axisZ = axis.get2();
866
    float casted, xoff=0, yoff=0, zoff=0;
867

    
868
    if( cubitType!=TwistyObjectCubit.TYPE_NORMAL )
869
      {
870
      xoff = mRowOffsets[puzzleFace][0];
871
      yoff = mRowOffsets[puzzleFace][1];
872
      zoff = mRowOffsets[puzzleFace][2];
873
      }
874

    
875
    for(int i=0; i<len; i++)
876
      {
877
      casted = (pos[3*i]+xoff)*axisX + (pos[3*i+1]+yoff)*axisY + (pos[3*i+2]+zoff)*axisZ;
878
      ret |= computeSingleRow(axisIndex,casted);
879
      }
880

    
881
    return ret;
882
    }
883

    
884
///////////////////////////////////////////////////////////////////////////////////////////////////
885

    
886
  private int computeSingleRow(int axisIndex,float casted)
887
    {
888
    int num = mNumCuts[axisIndex];
889
    float[] cuts = mCuts[axisIndex];
890

    
891
    for(int i=0; i<num; i++)
892
      {
893
      if( casted<cuts[i] ) return (1<<i);
894
      }
895

    
896
    return (1<<num);
897
    }
898

    
899
///////////////////////////////////////////////////////////////////////////////////////////////////
900

    
901
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
902
    {
903
    return (mCubits[cubit].getRotRow(axis) & rowBitmap) != 0;
904
    }
905

    
906
///////////////////////////////////////////////////////////////////////////////////////////////////
907
// note the minus in front of the sin() - we rotate counterclockwise
908
// when looking towards the direction where the axis increases in values.
909

    
910
  private Static4D makeQuaternion(float axisX, float axisY, float axisZ, int angleInDegrees)
911
    {
912
    while( angleInDegrees<0 ) angleInDegrees += 360;
913
    angleInDegrees %= 360;
914
    
915
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
916
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
917

    
918
    return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
919
    }
920

    
921
///////////////////////////////////////////////////////////////////////////////////////////////////
922

    
923
  void rotateAllCubits(int axisIndex, int rowBitmap, int angle)
924
    {
925
    Static3D axis = mAxis[axisIndex];
926
    float axisX = axis.get0();
927
    float axisY = axis.get1();
928
    float axisZ = axis.get2();
929
    Static4D quat = makeQuaternion(axisX,axisY,axisZ,angle);
930

    
931
    for(int i=0; i<mNumCubits; i++)
932
      {
933
      mBelongs[i] = belongsToRotation(i,axisIndex,rowBitmap);
934
      if( mBelongs[i] )
935
        {
936
        boolean result = mCubits[i].rotateCubit(quat,false);
937
        if( !result ) debugQuat(quat,i,axisX,axisY,axisZ,angle,1);
938
        }
939
      }
940

    
941
    recomputeFaceOffsets();
942

    
943
    for(int i=0; i<mNumCubits; i++)
944
      {
945
      if( mBelongs[i] )
946
        {
947
        int index = mCubits[i].postRotateCubit(quat);
948
        setCubitQuat(i,index);
949
        }
950
      else if( mCubits[i].getType()==TwistyObjectCubit.TYPE_FOLLOWER )
951
        {
952
        mCubits[i].computeRotationRow();
953
        setCubitQuat(i,mCubits[i].mQuatIndex);
954
        }
955
      }
956
    }
957

    
958
///////////////////////////////////////////////////////////////////////////////////////////////////
959

    
960
  private synchronized void setupPosition(int[][] moves)
961
    {
962
    if( moves!=null )
963
      {
964
      int axisIndex, row, rowBitmap, basic, angle;
965

    
966
      for(int[] move: moves)
967
        {
968
        axisIndex= move[0];
969
        rowBitmap= computeBandagedBitmap( move[1],axisIndex);
970
        row      = computeRowFromBitmap( move[1] );
971
        basic    = mBasicAngles[axisIndex][row];
972
        angle    = move[2]*(360/basic);   // this assumes that all layers from
973
                                          // the bitmap have the same BasicAngle.
974
                                          // at the moment this is always true as
975
                                          // there are no bandaged objects with
976
                                          // different per-layer BasicAngles.
977

    
978
        rotateAllCubits(axisIndex,rowBitmap,angle);
979
        }
980

    
981
      for(int i=0; i<mNumCubits; i++)
982
        {
983
        float[] pos = mCubits[i].getCurrentPos();
984
        int len = pos.length/3;
985
        for(int j=0; j<len; j++) clampPos(pos,3*j);
986
        }
987
      }
988
    }
989

    
990
///////////////////////////////////////////////////////////////////////////////////////////////////
991

    
992
  public int getScrambleType()
993
    {
994
    return ObjectScrambler.SCRAMBLING_ALGORITHMS;
995
    }
996

    
997
///////////////////////////////////////////////////////////////////////////////////////////////////
998

    
999
  public int[][] getMinimalCubiesInRow()
1000
    {
1001
    return null;
1002
    }
1003

    
1004
///////////////////////////////////////////////////////////////////////////////////////////////////
1005

    
1006
  int computeBandagedBitmap(int rowBitmap, int axisIndex)
1007
    {
1008
    if( mIsBandaged )
1009
      {
1010
      int bitmap, initBitmap=0;
1011

    
1012
      while( initBitmap!=rowBitmap )
1013
        {
1014
        initBitmap = rowBitmap;
1015

    
1016
        for(int cubit=0; cubit<mNumCubits; cubit++)
1017
          {
1018
          bitmap = mCubits[cubit].getRotRow(axisIndex);
1019
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
1020
          }
1021
        }
1022
      }
1023

    
1024
    if( mMinimalCubiesInRow!=null )
1025
      {
1026
      int[] minC = mMinimalCubiesInRow[axisIndex];
1027
      int numL = minC.length;
1028
      int[] numC = new int[numL];
1029

    
1030
      for(int cubit=0; cubit<mNumCubits; cubit++)
1031
        {
1032
        int bitmap = mCubits[cubit].getRotRow(axisIndex);
1033

    
1034
        for(int i=0; i<numL; i++)
1035
          {
1036
          if( (bitmap&0x1)!=0 ) numC[i]++;
1037
          bitmap>>=1;
1038
          }
1039
        }
1040

    
1041
      int bitmap,initBitmap = 0;
1042

    
1043
      while( initBitmap!=rowBitmap )
1044
        {
1045
        initBitmap = rowBitmap;
1046
        bitmap = rowBitmap;
1047
        int last = 0;
1048

    
1049
        for(int i=0; i<numL; i++)
1050
          {
1051
          if( numC[i]<minC[i] && numC[i]>0 )
1052
            {
1053
            if( (bitmap&0x1)!=0 )
1054
              {
1055
              if( i>0     ) rowBitmap |= (1<<(i-1));
1056
              if( i<numL-1) rowBitmap |= (1<<(i+1));
1057
              }
1058
            else if( (bitmap&0x2)!=0 || last>0 ) // we are not rotating this row, but
1059
                                                 // we are rotating the next or the prev
1060
              {
1061
              rowBitmap |= (1<<i);
1062
              }
1063
            }
1064

    
1065
          last = (bitmap&0x1);
1066
          bitmap>>=1;
1067
          }
1068
        }
1069
      }
1070

    
1071
    return rowBitmap;
1072
    }
1073

    
1074
///////////////////////////////////////////////////////////////////////////////////////////////////
1075

    
1076
  private int computeRowFromBitmap(int rowBitmap)
1077
    {
1078
    int index = 0;
1079

    
1080
    while(index<32)
1081
      {
1082
      if( (rowBitmap&0x1) != 0 ) return index;
1083
      rowBitmap>>=1;
1084
      index++;
1085
      }
1086
    return 0;
1087
    }
1088

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

    
1093
  void clampPos(float[] pos, int offset)
1094
    {
1095
    float currError, minError = Float.MAX_VALUE;
1096
    int minErrorIndex1 = -1;
1097
    int minErrorIndex2 = -1;
1098

    
1099
    float x = pos[offset  ];
1100
    float y = pos[offset+1];
1101
    float z = pos[offset+2];
1102

    
1103
    float xo,yo,zo;
1104

    
1105
    for(int i=0; i<mNumCubits; i++)
1106
      {
1107
      int len = mOrigPos[i].length / 3;
1108

    
1109
      for(int j=0; j<len; j++)
1110
        {
1111
        xo = mOrigPos[i][3*j  ];
1112
        yo = mOrigPos[i][3*j+1];
1113
        zo = mOrigPos[i][3*j+2];
1114

    
1115
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
1116

    
1117
        if( currError<minError )
1118
          {
1119
          minError = currError;
1120
          minErrorIndex1 = i;
1121
          minErrorIndex2 = j;
1122
          }
1123
        }
1124
      }
1125

    
1126
    if( minError< 0.05f ) // TODO: 0.05 ?
1127
      {
1128
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
1129
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
1130
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
1131
      }
1132
    }
1133

    
1134
///////////////////////////////////////////////////////////////////////////////////////////////////
1135

    
1136
  void setLibInterface(ObjectLibInterface inter)
1137
    {
1138
    mInterface = inter;
1139
    }
1140

    
1141
///////////////////////////////////////////////////////////////////////////////////////////////////
1142

    
1143
  void applyScrambles(int[][] moves)
1144
    {
1145
    setupPosition(moves);
1146
    }
1147

    
1148
///////////////////////////////////////////////////////////////////////////////////////////////////
1149

    
1150
  void initializeObject(int[][] moves)
1151
    {
1152
    solve();
1153
    setupPosition(moves);
1154
    }
1155

    
1156
///////////////////////////////////////////////////////////////////////////////////////////////////
1157

    
1158
  private void recomputeFaceOffsets()
1159
    {
1160
    if( mThereAreDeciders )
1161
      {
1162
      for(int i=0; i<mNumPuzzleFaces; i++)
1163
        {
1164
        mRowOffsets[i][0] =0;
1165
        mRowOffsets[i][1] =0;
1166
        mRowOffsets[i][2] =0;
1167
        }
1168

    
1169
      for(int i=0; i<mNumCubits; i++)
1170
        if( mCubits[i].getType()==TwistyObjectCubit.TYPE_DECIDER )
1171
          {
1172
          float[] offset = mCubits[i].getOffset();
1173
          int face = mCubits[i].getPuzzleFace();
1174
          mRowOffsets[face][0] = offset[0];
1175
          mRowOffsets[face][1] = offset[1];
1176
          mRowOffsets[face][2] = offset[2];
1177
          }
1178
      }
1179
    }
1180

    
1181
///////////////////////////////////////////////////////////////////////////////////////////////////
1182

    
1183
  synchronized float removeRotation()
1184
    {
1185
    return mRotation.removeRotation();
1186
    }
1187

    
1188
///////////////////////////////////////////////////////////////////////////////////////////////////
1189

    
1190
  long finishRotation(ObjectPreRender pre, int finishAx, int finishRow, float finishAngle, float avgSpeed)
1191
    {
1192
    int basicAngle = mBasicAngles[finishAx][finishRow];
1193
    return mRotation.finishRotation(pre,finishAx,finishRow,basicAngle,finishAngle,avgSpeed);
1194
    }
1195

    
1196
///////////////////////////////////////////////////////////////////////////////////////////////////
1197

    
1198
  void continueRotation(float angleInDegrees)
1199
    {
1200
    mRotation.continueRotation(angleInDegrees);
1201
    }
1202

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

    
1205
  synchronized long addRotation(EffectListener listener, int axis, int rowBitmap, int angle, long durationMillis )
1206
    {
1207
    return mRotation.addRotation(listener,axis,rowBitmap,angle,durationMillis);
1208
    }
1209

    
1210
///////////////////////////////////////////////////////////////////////////////////////////////////
1211

    
1212
  void enableGhostAxis(int axNum, boolean enable)
1213
    {
1214
    mTouchControl.enableGhostAxis(axNum,enable);
1215
    }
1216

    
1217
///////////////////////////////////////////////////////////////////////////////////////////////////
1218

    
1219
  synchronized boolean beginRotation(int axis, int row )
1220
    {
1221
    if( axis<0 || axis>=mNumAxis )
1222
      {
1223
      android.util.Log.e("object", "invalid rotation axis: "+axis);
1224
      return false;
1225
      }
1226

    
1227
    return mRotation.beginRotation(axis,row);
1228
    }
1229

    
1230
///////////////////////////////////////////////////////////////////////////////////////////////////
1231

    
1232
  void setTextureMap(int cubit, int face, int color)
1233
    {
1234
    int variant  = getCubitVariant(cubit,mNumLayers);
1235
    int shape    = getVariantStickerShape(variant,face);
1236
    int texIndex = color<0 || shape<0 ? mNumTextures-mNumOverrides : shape*mNumFaceColors + color;
1237
    int row      = (mNumTexRows-1) - texIndex/mNumTexCols;
1238
    int col      = texIndex%mNumTexCols;
1239

    
1240
    final float ratioW = 1.0f/mNumTexCols;
1241
    final float ratioH = 1.0f/mNumTexRows;
1242
    final Static4D[] maps = new Static4D[1];
1243
    maps[0] = new Static4D(col*ratioW, row*ratioH, ratioW, ratioH);
1244
    mMesh.setTextureMap(maps,mNumCubitFaces*cubit+face);
1245
    }
1246

    
1247
///////////////////////////////////////////////////////////////////////////////////////////////////
1248
// figure out the whole face to which (cubit,face) belongs, repaint all (cubit,face) pairs to
1249
// the new color.
1250

    
1251
  void repaintPuzzleFace(int cubit, int face, int newColor)
1252
    {
1253
    int oldColorIndex = getCubitFaceColor(cubit,face);
1254

    
1255
    if( oldColorIndex<0 || oldColorIndex>=mNumFaceColors )
1256
      {
1257
      android.util.Log.e("D", "error in TwistyObject.repaintPuzzleFace, index="+oldColorIndex);
1258
      return;
1259
      }
1260

    
1261
    int oldColor = mColorTable[oldColorIndex];
1262

    
1263
    if( oldColor!=newColor )
1264
      {
1265
      oldColor = mOriginalColorTable[oldColorIndex];
1266
      changeColorInTexture(oldColor,newColor);
1267
      mColorTable[oldColorIndex] = newColor;
1268
      setTexture();
1269

    
1270
      int numOrig = numberOfDifferentColors(mOriginalColorTable);
1271
      int numNow  = numberOfDifferentColors(mColorTable);
1272
      mCurrentColorSchemeSubmittable = (numOrig==numNow);
1273
      }
1274
    }
1275

    
1276
///////////////////////////////////////////////////////////////////////////////////////////////////
1277
// this doesn't have to return the real, displayed color - but the default one (from Shape classes).
1278
// The real displayed color can be different because of the sticker color overrides in 'Config'.
1279

    
1280
  int getCubitFaceColor(int cubit, int face)
1281
    {
1282
    int puzzleFace = getCubitFaceMap(cubit,face);
1283
    if( puzzleFace>=0 ) puzzleFace %= mNumFaceColors;
1284
    return puzzleFace;
1285
    }
1286

    
1287
///////////////////////////////////////////////////////////////////////////////////////////////////
1288

    
1289
  public int getCubitFaceMap(int cubit, int face)
1290
    {
1291
    int numFaces = mCubitFaceColors[cubit].length;
1292
    int puzzleFace = face<numFaces ? mCubitFaceColors[cubit][face] : -1;
1293
    return puzzleFace<0 ? -1 : puzzleFace;
1294
    }
1295

    
1296
///////////////////////////////////////////////////////////////////////////////////////////////////
1297
// this would return the REAL value of outer; the 'getCubitFaceColor()>=0' returns if the face is
1298
// colored, which for example in case of Container (which has colored internal faces) is not the
1299
// same thing
1300

    
1301
  boolean faceIsOuter(int cubit, int face)
1302
    {
1303
    if( mCubitFaceColors==null )
1304
      {
1305
      if( mShapes==null )
1306
        {
1307
        mShapes = new ObjectShape[mNumCubitVariants];
1308
        }
1309

    
1310
      if( mShapes[0]==null )
1311
        {
1312
        for(int v=0; v<mNumCubitVariants; v++) mShapes[v] = getObjectShape(v);
1313
        }
1314

    
1315
      if( mOrigQuat==null )
1316
        {
1317
        mOrigQuat = new Static4D[mNumCubits];
1318
        }
1319

    
1320
      if( mOrigQuat[0]==null )
1321
        {
1322
        for(int c=0; c<mNumCubits; c++) mOrigQuat[c] = getCubitQuats(c,mNumLayers);
1323
        }
1324

    
1325
      mCubitFaceColors = ObjectShape.computeColors(mShapes,mOrigPos,mOrigQuat,this);
1326
      }
1327

    
1328
    return mCubitFaceColors[cubit][face]>=0;
1329
    }
1330

    
1331
///////////////////////////////////////////////////////////////////////////////////////////////////
1332

    
1333
  void resetAllTextureMaps()
1334
    {
1335
    final float ratioW = 1.0f/mNumTexCols;
1336
    final float ratioH = 1.0f/mNumTexRows;
1337
    int cubColor, stiShape, texIndex, variant, row, col;
1338

    
1339
    for(int cubit=0; cubit<mNumCubits; cubit++)
1340
      {
1341
      final Static4D[] maps = new Static4D[mNumCubitFaces];
1342
      variant = getCubitVariant(cubit,mNumLayers);
1343

    
1344
      for(int face=0; face<mNumCubitFaces; face++)
1345
        {
1346
        cubColor = getCubitFaceColor(cubit,face);
1347
        stiShape = getVariantStickerShape(variant,face);
1348
        texIndex = cubColor<0 || stiShape<0 ? mNumTextures-mNumOverrides : stiShape*mNumFaceColors + cubColor;
1349
        row      = (mNumTexRows-1) - texIndex/mNumTexCols;
1350
        col      = texIndex%mNumTexCols;
1351

    
1352
        maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
1353
        }
1354

    
1355
      mMesh.setTextureMap(maps,mNumCubitFaces*cubit);
1356
      }
1357

    
1358
    overrideCubitFaceColor();
1359
    }
1360

    
1361
///////////////////////////////////////////////////////////////////////////////////////////////////
1362

    
1363
  private void overrideCubitFaceColor()
1364
    {
1365
    final float ratioW = 1.0f/mNumTexCols;
1366
    final float ratioH = 1.0f/mNumTexRows;
1367

    
1368
    for(int i=0; i<mNumOverrides; i++)
1369
      {
1370
      int[] cubitFaces = mStickerOverrides[i].getCubitFaces();
1371
      int length = cubitFaces.length/2;
1372

    
1373
      for(int j=0; j<length; j++)
1374
        {
1375
        final Static4D[] maps = new Static4D[1];
1376
        int color = mNumTextures-mNumOverrides+1+i;
1377
        int row   = (mNumTexRows-1) - color/mNumTexCols;
1378
        int col   = color%mNumTexCols;
1379
        int cubit = cubitFaces[2*j];
1380
        int face  = cubitFaces[2*j+1];
1381
        maps[0] = new Static4D(col*ratioW, row*ratioH, ratioW, ratioH);
1382
        mMesh.setTextureMap(maps,mNumCubitFaces*cubit+face);
1383
        }
1384
      }
1385
    }
1386

    
1387
///////////////////////////////////////////////////////////////////////////////////////////////////
1388

    
1389
  void releaseResources()
1390
    {
1391
    mTexture.markForDeletion();
1392
    mMesh.markForDeletion();
1393
    mEffects.markForDeletion();
1394

    
1395
    if( mBitmap!=null )
1396
      {
1397
      mBitmap.recycle();
1398
      mBitmap = null;
1399
      }
1400

    
1401
    for(int j=0; j<mNumCubits; j++)
1402
      {
1403
      mCubits[j].releaseResources();
1404
      }
1405
    }
1406

    
1407
///////////////////////////////////////////////////////////////////////////////////////////////////
1408

    
1409
  private void setCubitQuat(int cubit, int equAssociation)
1410
    {
1411
    int andAssociation = mCubits[cubit].computeAssociation();
1412

    
1413
    if( !mIsInMixupMode )
1414
      {
1415
      mMesh.setEffectAssociation(cubit,andAssociation,equAssociation);
1416
      }
1417
    else
1418
      {
1419
      mMesh.setEffectAssociation(cubit,andAssociation,cubit);
1420
      Static4D tmp = mObjectQuats[equAssociation];
1421
      mMixupModeQuats[cubit].set(tmp);
1422
      }
1423
    }
1424

    
1425
///////////////////////////////////////////////////////////////////////////////////////////////////
1426

    
1427
  private int numberOfDifferentColors(int[] table)
1428
    {
1429
    int ret=0;
1430
    int len = table.length;
1431

    
1432
    for(int i=0; i<len; i++)
1433
      {
1434
      boolean increase = true;
1435

    
1436
      for(int j=0; j<i; j++)
1437
        if( table[j]==table[i] )
1438
          {
1439
          increase = false;
1440
          break;
1441
          }
1442

    
1443
      if( increase ) ret++;
1444
      }
1445

    
1446
    return ret;
1447
    }
1448

    
1449
///////////////////////////////////////////////////////////////////////////////////////////////////
1450

    
1451
  private void restoreSti(String key, OperatingSystemInterface os)
1452
    {
1453
    mTextureBorderMultiplier = os.getFloat(key+"_border", 1.0f);
1454
    mTextureCornerMultiplier = os.getFloat(key+"_corner", 1.0f);
1455
    String colors = os.getString(key+"_colors", null);
1456
    boolean different = false;
1457

    
1458
    if( colors!=null ) different = restoreColors(colors,mColorTable);
1459

    
1460
    if( different || mTextureBorderMultiplier!=1.0f || mTextureCornerMultiplier!=1.0f )
1461
      {
1462
      createTexture(mTextureBorderMultiplier,mTextureCornerMultiplier);
1463
      setTexture();
1464
      mSolved.setPuzzleFaceColor(mColorTable);
1465

    
1466
      int numOrig = numberOfDifferentColors(mOriginalColorTable);
1467
      int numNow  = numberOfDifferentColors(mColorTable);
1468
      mCurrentColorSchemeSubmittable = (numOrig==numNow);
1469
      }
1470
    }
1471

    
1472
///////////////////////////////////////////////////////////////////////////////////////////////////
1473

    
1474
  synchronized void restorePreferences(OperatingSystemInterface os)
1475
    {
1476
    boolean error = false;
1477
    String key = getShortName();
1478

    
1479
    for(int i=0; i<mNumCubits; i++)
1480
      {
1481
      mQuatDebug[i] = mCubits[i].restorePreferences(key,os);
1482

    
1483
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1484
        {
1485
        boolean result = mCubits[i].rotateCubit(mObjectQuats[mQuatDebug[i]],true);
1486
        if( !result ) debugQuat(mObjectQuats[mQuatDebug[i]],i,0,0,0,mQuatDebug[i],3);
1487
        }
1488
      else { error = true; break; }
1489
      }
1490

    
1491
    if( !error )
1492
      {
1493
      recomputeFaceOffsets();
1494

    
1495
      for(int i=0; i<mNumCubits; i++)
1496
        {
1497
        if( mQuatDebug[i]>=0 && mQuatDebug[i]<mNumQuats )
1498
          {
1499
          mCubits[i].computeRotationRow();
1500
          setCubitQuat(i,mQuatDebug[i]);
1501
          }
1502
        else { error = true; break; }
1503
        }
1504
      }
1505

    
1506
    if( error )
1507
      {
1508
      for(int i=0; i<mNumCubits; i++)
1509
        {
1510
        mCubits[i].solve();
1511
        setCubitQuat(i,0);
1512
        }
1513
      }
1514

    
1515
    restoreSti(key,os);
1516
    }
1517

    
1518
///////////////////////////////////////////////////////////////////////////////////////////////////
1519

    
1520
  synchronized void restoreStickers(OperatingSystemInterface os)
1521
    {
1522
    String key = getShortName();
1523
    restoreSti(key,os);
1524
    }
1525

    
1526
///////////////////////////////////////////////////////////////////////////////////////////////////
1527

    
1528
  void savePreferences(OperatingSystemInterface os)
1529
    {
1530
    String key = getShortName();
1531
    for(int i=0; i<mNumCubits; i++) mCubits[i].savePreferences(key,os);
1532

    
1533
    os.putFloat(key+"_border", mTextureBorderMultiplier);
1534
    os.putFloat(key+"_corner", mTextureCornerMultiplier);
1535
    os.putString(key+"_colors", createColors(mColorTable) );
1536
    }
1537

    
1538
///////////////////////////////////////////////////////////////////////////////////////////////////
1539

    
1540
  public void removePreferences(OperatingSystemInterface os)
1541
    {
1542
    String key = getShortName();
1543
    for(int i=0; i<mNumCubits; i++) mCubits[i].removePreferences(key,os);
1544

    
1545
    os.remove(key+"_border");
1546
    os.remove(key+"_corner");
1547
    os.remove(key+"_colors");
1548
    }
1549

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

    
1552
  private String createColors(int[] table)
1553
    {
1554
    StringBuilder sb = new StringBuilder();
1555
    int len = table!=null ? table.length : 0;
1556

    
1557
    for(int i=0; i<len; i++)
1558
      {
1559
      sb.append(table[i]);
1560
      sb.append(',');
1561
      }
1562

    
1563
    return sb.toString();
1564
    }
1565

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

    
1568
  private boolean restoreColors(String colors, int[] table)
1569
    {
1570
    String[] parts = colors.split(",");
1571
    int len = parts.length;
1572
    boolean ret = false;
1573

    
1574
    try
1575
      {
1576
      for(int s=0; s<len; s++)
1577
        {
1578
        table[s] = Integer.parseInt(parts[s]);
1579
        if( table[s]!=mOriginalColorTable[s] ) ret = true;
1580
        }
1581
      }
1582
    catch(NumberFormatException nfe)
1583
      {
1584
      for(int s=0; s<len; s++) table[s] = mOriginalColorTable[s];
1585
      return false;
1586
      }
1587

    
1588
    return ret;
1589
    }
1590

    
1591
///////////////////////////////////////////////////////////////////////////////////////////////////
1592

    
1593
  private float computeRadiusCorrection(float[][] sticker, int curr, int len)
1594
    {
1595
    final float A = 0.8f;  // 0<A<1
1596

    
1597
    int prev = curr>0 ? curr-1 : len-1;
1598
    int next = curr<len-1 ? curr+1 : 0;
1599

    
1600
    float v1x = sticker[prev][0]-sticker[curr][0];
1601
    float v1y = sticker[prev][1]-sticker[curr][1];
1602
    float v2x = sticker[next][0]-sticker[curr][0];
1603
    float v2y = sticker[next][1]-sticker[curr][1];
1604

    
1605
    float len1= v1x*v1x+v1y*v1y;
1606
    float len2= v2x*v2x+v2y*v2y;
1607

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

    
1610
    return 1-A*cos;
1611
    }
1612

    
1613
///////////////////////////////////////////////////////////////////////////////////////////////////
1614
// Radius of the sphere circumscribed on the puzzle. Needed for pillowing.
1615
//
1616
// This won't work correctly for pillowing off-center puzzles (e.g. mirrors) - for those we'd need
1617
// to introduce the concept of a 'sink center' as well.
1618
//
1619
// public because needed in TouchControlShapemod
1620

    
1621
  public float getCircumscribedRadius()
1622
    {
1623
    switch(mNumPuzzleFaces)
1624
      {
1625
      case  4: return (SQ6/4)*mSize;
1626
      case  6: return (SQ3/2)*mSize;
1627
      case  8: return (SQ2/2)*mSize;
1628
      case 12: return (SQ3/2)*((SQ5+1)/2)*mSize;
1629
      case 16: return 0.50f*mSize;
1630
      }
1631

    
1632
    return 0.0f;
1633
    }
1634

    
1635
///////////////////////////////////////////////////////////////////////////////////////////////////
1636
// might be overridden in subclasses which want per-edge radii
1637

    
1638
  protected float[][][] getStickerRadii()
1639
    {
1640
    float radius = getStickerRadius();
1641
    int numStickers = mStickerCoords.length;
1642
    float[][][] ret = new float[numStickers][][];
1643

    
1644
    for(int s=0; s<numStickers; s++)
1645
      {
1646
      int numLoops = mStickerCoords[s].length;
1647
      ret[s] = new float[numLoops][];
1648

    
1649
      for(int l=0; l<numLoops; l++)
1650
        {
1651
        int numVertices = mStickerCoords[s][l].length;
1652
        ret[s][l] = new float[numVertices];
1653
        for(int v=0; v<numVertices; v++) ret[s][l][v] = radius;
1654
        }
1655
      }
1656

    
1657
    return ret;
1658
    }
1659

    
1660
///////////////////////////////////////////////////////////////////////////////////////////////////
1661
// might be overridden in subclasses which want per-edge strokes
1662

    
1663
  protected float[][][] getStickerStrokes()
1664
    {
1665
    float stroke = getStickerStroke();
1666
    int numStickers = mStickerCoords.length;
1667
    float[][][] ret = new float[numStickers][][];
1668

    
1669
    for(int s=0; s<numStickers; s++)
1670
      {
1671
      int numLoops = mStickerCoords[s].length;
1672
      ret[s] = new float[numLoops][];
1673

    
1674
      for(int l=0; l<numLoops; l++)
1675
        {
1676
        int numVertices = mStickerCoords[s][l].length;
1677
        ret[s][l] = new float[numVertices];
1678
        for(int v=0; v<numVertices; v++) ret[s][l][v] = stroke;
1679
        }
1680
      }
1681

    
1682
    return ret;
1683
    }
1684

    
1685
///////////////////////////////////////////////////////////////////////////////////////////////////
1686

    
1687
  public ObjectSticker retSticker(int sticker)
1688
    {
1689
    if( mStickers==null )
1690
      {
1691
      float[][][] radii   = getStickerRadii();
1692
      float[][][] strokes = getStickerStrokes();
1693
      float[][][] angles  = getStickerAngles();
1694
      int numStickers = mStickerCoords.length;
1695
      mStickers = new ObjectSticker[numStickers];
1696

    
1697
      for(int s=0; s<numStickers; s++)
1698
        {
1699
        float scale = mStickerScales.length>s ? mStickerScales[s] : 1.0f;
1700
        int numLoops = mStickerCoords[s].length;
1701
        float[][] rad = new float[numLoops][];
1702
        float[][] str = new float[numLoops][];
1703

    
1704
        for(int l=0; l<numLoops; l++)
1705
          {
1706
          int numVerts = mStickerCoords[s][l].length;
1707
          rad[l] = new float[numVerts];
1708
          str[l] = new float[numVerts];
1709
          float[] st = strokes[s][l];
1710
          float[] ra = radii[s][l];
1711

    
1712
          for(int v=0; v<numVerts; v++)
1713
            {
1714
            rad[l][v] = ra[v] * computeRadiusCorrection(mStickerCoords[s][l],v,numVerts) / scale;
1715
            str[l][v] = st[v] / scale;
1716
            }
1717
          }
1718

    
1719
        mStickers[s] = new ObjectSticker(mStickerCoords[s], (angles==null ? null : angles[s]) ,rad, str);
1720
        }
1721
      }
1722

    
1723
    return mStickers[sticker];
1724
    }
1725

    
1726
///////////////////////////////////////////////////////////////////////////////////////////////////
1727
// some objects (currently Kilominx,Ivy,Rex) might want to change the stickers.
1728

    
1729
  public void adjustStickerCoords()
1730
    {
1731

    
1732
    }
1733

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

    
1736
  public Static4D[] getQuats()
1737
    {
1738
    if( mObjectQuats==null )
1739
      {
1740
      mObjectQuats = QuatGroupGenerator.computeGroup(mAxis,mBasicAngles);
1741
      }
1742

    
1743
    return mObjectQuats;
1744
    }
1745

    
1746
///////////////////////////////////////////////////////////////////////////////////////////////////
1747

    
1748
  public int[][] getVariantFaceIsOuter()
1749
    {
1750
    return mVariantFaceIsOuter;
1751
    }
1752

    
1753
///////////////////////////////////////////////////////////////////////////////////////////////////
1754

    
1755
  public int getInternalColor()
1756
    {
1757
    return COLOR_INTERNAL;
1758
    }
1759

    
1760
///////////////////////////////////////////////////////////////////////////////////////////////////
1761

    
1762
  public float getTextureBorders()
1763
    {
1764
    return mTextureBorderMultiplier;
1765
    }
1766

    
1767
///////////////////////////////////////////////////////////////////////////////////////////////////
1768

    
1769
  public float getTextureCorners()
1770
    {
1771
    return mTextureCornerMultiplier;
1772
    }
1773

    
1774
///////////////////////////////////////////////////////////////////////////////////////////////////
1775
// the getFaceColors + final INTERNAL_COLOR in a grid (so that we do not exceed the maximum texture size)
1776

    
1777
  void createTexture(float border, float corner)
1778
    {
1779
    Paint paint = new Paint();
1780
    Canvas canvas = new Canvas(mBitmap);
1781

    
1782
    paint.setAntiAlias(true);
1783
    paint.setTextAlign(Paint.Align.CENTER);
1784
    paint.setStyle(Paint.Style.FILL);
1785
    paint.setColor(getInternalColor());
1786
    canvas.drawRect(0, 0, mNumTexCols*mTexHeight, mNumTexRows*mTexHeight, paint);
1787

    
1788
    mTextureBorderMultiplier = border;
1789
    mTextureCornerMultiplier = corner;
1790

    
1791
    int texture = 0;
1792
    FactorySticker factory = FactorySticker.getInstance();
1793

    
1794
    for(int row=0; row<mNumTexRows; row++)
1795
      for(int col=0; col<mNumTexCols; col++)
1796
        {
1797
        if( texture<mNumTextures-mNumOverrides )
1798
          {
1799
          ObjectSticker sticker = retSticker(texture/mNumFaceColors);
1800
          int color = mColorTable[texture%mNumFaceColors];
1801
          factory.drawRoundedPolygons( canvas, paint, col*mTexHeight, (mNumTexRows-row)*mTexHeight, color,
1802
                                       mTexHeight, sticker,mTextureBorderMultiplier,mTextureCornerMultiplier);
1803
          }
1804
        else if( texture>mNumTextures-mNumOverrides && texture<=mNumTextures )
1805
          {
1806
          int color = mStickerOverrides[mNumTextures-texture].getColor();
1807
          factory.drawSolidColor(canvas, paint, col*mTexHeight, (mNumTexRows-row)*mTexHeight, color, mTexHeight);
1808
          }
1809

    
1810
        texture++;
1811
        }
1812
    }
1813

    
1814
///////////////////////////////////////////////////////////////////////////////////////////////////
1815

    
1816
  private void changeColorInTexture(int oldColor, int newColor)
1817
    {
1818
    Paint paint = new Paint();
1819
    Canvas canvas = new Canvas(mBitmap);
1820
    paint.setAntiAlias(true);
1821
    int texture = 0;
1822
    FactorySticker factory = FactorySticker.getInstance();
1823

    
1824
    for(int row=0; row<mNumTexRows; row++)
1825
      for(int col=0; col<mNumTexCols; col++)
1826
        {
1827
        if( texture<mNumTextures-mNumOverrides )
1828
          {
1829
          int color = mOriginalColorTable[texture%mNumFaceColors];
1830

    
1831
          if( color==oldColor )
1832
            {
1833
            ObjectSticker sticker = retSticker(texture/mNumFaceColors);
1834
            factory.drawRoundedPolygons( canvas, paint, col*mTexHeight, (mNumTexRows-row)*mTexHeight,
1835
                                         newColor, mTexHeight, sticker,mTextureBorderMultiplier,mTextureCornerMultiplier);
1836
            }
1837
          }
1838

    
1839
        texture++;
1840
        }
1841
    }
1842

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

    
1845
  void setTexture()
1846
    {
1847
    if( mBitmap==null )
1848
      {
1849
      mBitmap = Bitmap.createBitmap( mNumTexCols*mTexHeight, mNumTexRows*mTexHeight, Bitmap.Config.ARGB_4444);
1850
      createTexture(mTextureBorderMultiplier,mTextureCornerMultiplier);
1851
      }
1852

    
1853
    if( !mTexture.setTextureAlreadyInverted(mBitmap) )
1854
      {
1855
      int max = DistortedLibrary.getMaxTextureSize();
1856
      mInterface.reportProblem("failed to set texture of size "+mBitmap.getWidth()+"x"+mBitmap.getHeight()+" max is "+max, true);
1857
      }
1858
    }
1859

    
1860
///////////////////////////////////////////////////////////////////////////////////////////////////
1861

    
1862
  void setObjectRatioNow(float sc, int nodeSize)
1863
    {
1864
    mObjectScreenRatio = sc;
1865
    float scale = mObjectScreenRatio*mInitScreenRatio*nodeSize/mSize;
1866
    mObjectScale.set(scale,scale,scale);
1867

    
1868
    if( mTouchControl ==null ) mTouchControl = getTouchControl();
1869
    mTouchControl.setObjectRatio(mObjectScreenRatio*mInitScreenRatio);
1870
    }
1871

    
1872
///////////////////////////////////////////////////////////////////////////////////////////////////
1873

    
1874
  void setObjectRatio(float sizeChange, int nodeSize)
1875
    {
1876
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
1877

    
1878
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
1879
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
1880

    
1881
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1882
    }
1883

    
1884
///////////////////////////////////////////////////////////////////////////////////////////////////
1885

    
1886
  void setNodeSize(int nodeSize)
1887
    {
1888
    setObjectRatioNow(mObjectScreenRatio, nodeSize);
1889
    }
1890

    
1891
///////////////////////////////////////////////////////////////////////////////////////////////////
1892

    
1893
  public float getRatio()
1894
    {
1895
    return mObjectScreenRatio;
1896
    }
1897

    
1898
///////////////////////////////////////////////////////////////////////////////////////////////////
1899

    
1900
  public float getObjectRatio()
1901
    {
1902
    return mObjectScreenRatio*mInitScreenRatio;
1903
    }
1904

    
1905
///////////////////////////////////////////////////////////////////////////////////////////////////
1906

    
1907
  boolean isSolved()
1908
    {
1909
    boolean solved = mSolved.isSolved(mCubits);
1910
    return mRotation.isSolved(solved);
1911
    }
1912

    
1913
///////////////////////////////////////////////////////////////////////////////////////////////////
1914
// INTERNAL API - those are called from 'effects' package
1915
///////////////////////////////////////////////////////////////////////////////////////////////////
1916

    
1917
  public void randomizeNewScramble(int[][] scramble, Random rnd, int curr, int total)
1918
    {
1919
    mScrambler.randomizeNewScramble(scramble,rnd,curr,total, getSignature() );
1920
    }
1921

    
1922
///////////////////////////////////////////////////////////////////////////////////////////////////
1923

    
1924
  public Static4D getRotationQuat()
1925
    {
1926
    return mQuat;
1927
    }
1928

    
1929
///////////////////////////////////////////////////////////////////////////////////////////////////
1930

    
1931
  public float getSize()
1932
    {
1933
    return mSize;
1934
    }
1935

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

    
1938
  public void applyEffect(Effect effect, int position)
1939
    {
1940
    mEffects.apply(effect, position);
1941
    }
1942

    
1943
///////////////////////////////////////////////////////////////////////////////////////////////////
1944

    
1945
  public void removeEffect(long effectID)
1946
    {
1947
    mEffects.abortById(effectID);
1948
    }
1949

    
1950
///////////////////////////////////////////////////////////////////////////////////////////////////
1951

    
1952
  public MeshBase getObjectMesh()
1953
    {
1954
    return mMesh;
1955
    }
1956

    
1957
///////////////////////////////////////////////////////////////////////////////////////////////////
1958

    
1959
  public DistortedEffects getObjectEffects()
1960
    {
1961
    return mEffects;
1962
    }
1963

    
1964
///////////////////////////////////////////////////////////////////////////////////////////////////
1965

    
1966
  public int getCubitType(int cubit)
1967
    {
1968
    return mCubits[cubit].getType();
1969
    }
1970

    
1971
///////////////////////////////////////////////////////////////////////////////////////////////////
1972

    
1973
  public float[] getCubitOffset(int cubit)
1974
    {
1975
    return mCubits[cubit].getOffset();
1976
    }
1977

    
1978
///////////////////////////////////////////////////////////////////////////////////////////////////
1979

    
1980
  public ObjectStickerOverride[] getStickerOverrides()
1981
    {
1982
    return null;
1983
    }
1984

    
1985
///////////////////////////////////////////////////////////////////////////////////////////////////
1986

    
1987
  public boolean getError()
1988
    {
1989
    return mError;
1990
    }
1991

    
1992
///////////////////////////////////////////////////////////////////////////////////////////////////
1993

    
1994
  public String getErrorString()
1995
    {
1996
    return mErrorString;
1997
    }
1998

    
1999
///////////////////////////////////////////////////////////////////////////////////////////////////
2000
// PUBLIC API
2001
///////////////////////////////////////////////////////////////////////////////////////////////////
2002

    
2003
  public int getCubitFaceStickerIndex(int cubit, int face)
2004
    {
2005
    Static4D texMap = mMesh.getTextureMap(mNumCubitFaces*cubit + face);
2006

    
2007
    int x = (int)(texMap.get0()/texMap.get2());
2008
    int y = (int)(texMap.get1()/texMap.get3());
2009

    
2010
    return (mNumTexRows-1-y)*mNumTexCols + x;
2011
    }
2012

    
2013
///////////////////////////////////////////////////////////////////////////////////////////////////
2014

    
2015
  public int[] getNumLayers()
2016
    {
2017
    return mNumLayers;
2018
    }
2019

    
2020
///////////////////////////////////////////////////////////////////////////////////////////////////
2021

    
2022
  public synchronized void solve()
2023
    {
2024
    for(int i=0; i<mNumCubits; i++) mCubits[i].solve();
2025

    
2026
    recomputeFaceOffsets();
2027

    
2028
    for(int i=0; i<mNumCubits; i++)
2029
      {
2030
      mCubits[i].computeRotationRow();
2031
      setCubitQuat(i,0);
2032
      }
2033
    }
2034

    
2035
///////////////////////////////////////////////////////////////////////////////////////////////////
2036

    
2037
  public int getCubitQuatIndex(int cubit)
2038
    {
2039
    return (cubit>=0 && cubit<mNumCubits) ? mCubits[cubit].mQuatIndex : 0;
2040
    }
2041

    
2042
///////////////////////////////////////////////////////////////////////////////////////////////////
2043

    
2044
  public int getCubitRotRow(int cubit, int axis)
2045
    {
2046
    return mCubits[cubit].getRotRow(axis);
2047
    }
2048

    
2049
///////////////////////////////////////////////////////////////////////////////////////////////////
2050

    
2051
  public Bitmap getStickerBitmap()
2052
    {
2053
    return mBitmap;
2054
    }
2055

    
2056
///////////////////////////////////////////////////////////////////////////////////////////////////
2057

    
2058
  public DistortedNode getNode()
2059
    {
2060
    return mNode;
2061
    }
2062

    
2063
///////////////////////////////////////////////////////////////////////////////////////////////////
2064

    
2065
  public int getNumStickerTypes()
2066
    {
2067
    return mNumStickerTypes;
2068
    }
2069

    
2070
///////////////////////////////////////////////////////////////////////////////////////////////////
2071

    
2072
  public String reportState()
2073
    {
2074
    StringBuilder builder = new StringBuilder();
2075

    
2076
    for(int i=0; i<mNumCubits; i++ )
2077
      {
2078
      if( i>0 ) builder.append('.');
2079
      builder.append(mCubits[i].mQuatIndex);
2080
      }
2081

    
2082
    return builder.toString();
2083
    }
2084

    
2085
///////////////////////////////////////////////////////////////////////////////////////////////////
2086
// this is here only so it can be overridden in TwistyJSON so that we can get this from JSON.
2087

    
2088
  public int getNumCubitFaces()
2089
    {
2090
    return 0;
2091
    }
2092

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

    
2098
  public float getPillowCoeff()
2099
    {
2100
    return 1.0f;
2101
    }
2102

    
2103
///////////////////////////////////////////////////////////////////////////////////////////////////
2104

    
2105
  public TouchControl getTouchControl()
2106
    {
2107
    if( mTouchControl==null )
2108
      {
2109
      switch(getTouchControlType())
2110
        {
2111
        case TC_TETRAHEDRON      : mTouchControl = new TouchControlTetrahedron(this);
2112
                                   break;
2113
        case TC_HEXAHEDRON       : mTouchControl = new TouchControlHexahedron(this);
2114
                                   break;
2115
        case TC_OCTAHEDRON       : mTouchControl = new TouchControlOctahedron(this);
2116
                                   break;
2117
        case TC_DODECAHEDRON     : mTouchControl = new TouchControlDodecahedron(this);
2118
                                   break;
2119
        case TC_ICOSAHEDRON      : mTouchControl = new TouchControlIcosahedron(this);
2120
                                   break;
2121
        case TC_CUBOID           : int[] numLayers = getNumLayers();
2122
                                   mTouchControl = new TouchControlCuboids(this,getDist3D(numLayers));
2123
                                   break;
2124
        case TC_BALL             : mTouchControl = new TouchControlBall(this);
2125
                                   break;
2126
        case TC_CHANGING_MIRROR  : mTouchControl = new TouchControlMirror(this);
2127
                                   break;
2128
        case TC_CHANGING_SQUARE  : mTouchControl = new TouchControlSquare(this);
2129
                                   break;
2130
        case TC_CHANGING_SHAPEMOD: mTouchControl = new TouchControlShapemod(this);
2131
                                   break;
2132
        }
2133
      }
2134
    return mTouchControl;
2135
    }
2136

    
2137
///////////////////////////////////////////////////////////////////////////////////////////////////
2138

    
2139
  public float[][] returnRotationFactor()
2140
    {
2141
    float[][] factor = new float[mNumAxis][];
2142

    
2143
    for(int ax=0; ax<mNumAxis; ax++)
2144
      {
2145
      int numL = mNumLayers[ax];
2146
      factor[ax] = new float[numL];
2147
      for(int la=0; la<numL; la++) factor[ax][la] = 1.0f;
2148
      }
2149

    
2150
    return factor;
2151
    }
2152

    
2153
///////////////////////////////////////////////////////////////////////////////////////////////////
2154

    
2155
  protected void setReader(JsonReader reader)
2156
    {
2157
    // empty
2158
    }
2159

    
2160
///////////////////////////////////////////////////////////////////////////////////////////////////
2161

    
2162
  public String getInventor()
2163
    {
2164
    return mMetadata.getAuthor();
2165
    }
2166

    
2167
///////////////////////////////////////////////////////////////////////////////////////////////////
2168

    
2169
  public int getYearOfInvention()
2170
    {
2171
    return mMetadata.getYearOfInvention();
2172
    }
2173

    
2174
///////////////////////////////////////////////////////////////////////////////////////////////////
2175

    
2176
  public float getComplexity()
2177
    {
2178
    return mMetadata.getDifficulty();
2179
    }
2180

    
2181
///////////////////////////////////////////////////////////////////////////////////////////////////
2182

    
2183
  public String getObjectName()
2184
    {
2185
    return mMetadata.getObjectName();
2186
    }
2187

    
2188
///////////////////////////////////////////////////////////////////////////////////////////////////
2189

    
2190
  public ObjectSignature getSignature()
2191
    {
2192
    return mMetadata.getSignature();
2193
    }
2194

    
2195
///////////////////////////////////////////////////////////////////////////////////////////////////
2196

    
2197
  public boolean isSubmittable()
2198
    {
2199
    return mCurrentColorSchemeSubmittable;
2200
    }
2201

    
2202
///////////////////////////////////////////////////////////////////////////////////////////////////
2203
  // for JSON only
2204
  public abstract int getTouchControlType();
2205
  public abstract int getTouchControlSplit();
2206
  public abstract boolean[][] getLayerRotatable(int[] numLayers);
2207
  public abstract int[][][] getEnabled();
2208
  public abstract float[] getDist3D(int[] numLayers);
2209
  public abstract Static3D[] getFaceAxis();
2210
  public abstract int[][] getScrambleEdges();
2211
  public abstract float[][] getCuts(int[] numLayers);
2212
  public abstract float getStickerRadius();
2213
  public abstract float getStickerStroke();
2214
  public abstract float[][][] getStickerAngles();
2215
  public abstract int getCubitVariant(int cubit, int[] numLayers);
2216
  public abstract ObjectShape getObjectShape(int variant);
2217
  public abstract ObjectFaceShape getObjectFaceShape(int variant);
2218
  public abstract ObjectVertexEffects getVertexEffects(int variant);
2219
  public abstract int getNumCubitVariants(int[] numLayers);
2220
  public abstract float[][] getCubitPositions(int[] numLayers);
2221
  public abstract Static4D getCubitQuats(int cubit, int[] numLayers);
2222
  public abstract float getScreenRatio();
2223
  public abstract int[] getColorTable();
2224
  public abstract String getShortName();
2225

    
2226
  // not only for JSON
2227
  public abstract Static3D[] getRotationAxis();
2228
  public abstract int[][] getBasicAngles();
2229
  public abstract int getNumPuzzleFaces();
2230
  public abstract int getFOV();
2231
  public abstract String[][] getTutorials();
2232
  }
(6-6/9)