Project

General

Profile

Download (28.6 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / objects / TwistyObject.java @ 722b2512

1 fdec60a3 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19
20 1f9772f3 Leszek Koltunski
package org.distorted.objects;
21 fdec60a3 Leszek Koltunski
22 27a70eae Leszek Koltunski
import android.content.SharedPreferences;
23 ccf9fec5 Leszek Koltunski
import android.content.res.Resources;
24 411c6285 Leszek Koltunski
import android.graphics.Bitmap;
25
import android.graphics.Canvas;
26
import android.graphics.Paint;
27 27a70eae Leszek Koltunski
28 27e6c301 Leszek Koltunski
import com.google.firebase.crashlytics.FirebaseCrashlytics;
29
30 27a70eae Leszek Koltunski
import org.distorted.library.effect.Effect;
31 19f0f767 Leszek Koltunski
import org.distorted.library.effect.MatrixEffectMove;
32 27a70eae Leszek Koltunski
import org.distorted.library.effect.MatrixEffectQuaternion;
33
import org.distorted.library.effect.MatrixEffectScale;
34 10585385 Leszek Koltunski
import org.distorted.library.effect.VertexEffectQuaternion;
35 27e6c301 Leszek Koltunski
import org.distorted.library.effect.VertexEffectRotate;
36 27a70eae Leszek Koltunski
import org.distorted.library.main.DistortedEffects;
37 c7e23561 Leszek Koltunski
import org.distorted.library.main.DistortedLibrary;
38 27a70eae Leszek Koltunski
import org.distorted.library.main.DistortedNode;
39
import org.distorted.library.main.DistortedTexture;
40 b32444ee Leszek Koltunski
import org.distorted.library.mesh.MeshBase;
41 ccf9fec5 Leszek Koltunski
import org.distorted.library.mesh.MeshFile;
42 19f0f767 Leszek Koltunski
import org.distorted.library.mesh.MeshJoined;
43 efa8aa48 Leszek Koltunski
import org.distorted.library.mesh.MeshSquare;
44 27a70eae Leszek Koltunski
import org.distorted.library.message.EffectListener;
45 27e6c301 Leszek Koltunski
import org.distorted.library.type.Dynamic1D;
46 27a70eae Leszek Koltunski
import org.distorted.library.type.Static1D;
47
import org.distorted.library.type.Static3D;
48
import org.distorted.library.type.Static4D;
49 25445dcf Leszek Koltunski
import org.distorted.main.BuildConfig;
50 221a4090 Leszek Koltunski
import org.distorted.main.RubikSurfaceView;
51 4f9f99a2 Leszek Koltunski
52 ccf9fec5 Leszek Koltunski
import java.io.DataInputStream;
53
import java.io.IOException;
54
import java.io.InputStream;
55 7c969a6d Leszek Koltunski
import java.util.Random;
56 ccf9fec5 Leszek Koltunski
57 0333d81e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
58
59 9c2f0c91 Leszek Koltunski
public abstract class TwistyObject extends DistortedNode
60 fdec60a3 Leszek Koltunski
  {
61 ece1b58d Leszek Koltunski
  static final int COLOR_YELLOW = 0xffffff00;
62
  static final int COLOR_WHITE  = 0xffffffff;
63
  static final int COLOR_BLUE   = 0xff0000ff;
64 323b217c Leszek Koltunski
  static final int COLOR_GREEN  = 0xff00bb00;
65 28b54fe3 Leszek Koltunski
  static final int COLOR_RED    = 0xff990000;
66 848c7953 Leszek Koltunski
  static final int COLOR_ORANGE = 0xffff6200;
67 33b4138b Leszek Koltunski
  static final int COLOR_GREY   = 0xff727c7b;
68 5581ba2b Leszek Koltunski
  static final int COLOR_VIOLET = 0xff7700bb;
69 ee526fe0 Leszek Koltunski
  static final int COLOR_BLACK  = 0xff000000;
70 ece1b58d Leszek Koltunski
71 b89898c5 Leszek Koltunski
  static final int TEXTURE_HEIGHT = 256;
72 ae755eda Leszek Koltunski
  static final int NUM_STICKERS_IN_ROW = 4;
73 b89898c5 Leszek Koltunski
74 3f3ff476 Leszek Koltunski
  static final float SQ2 = (float)Math.sqrt(2);
75
  static final float SQ3 = (float)Math.sqrt(3);
76 bbc6da6c Leszek Koltunski
  static final float SQ5 = (float)Math.sqrt(5);
77 3f3ff476 Leszek Koltunski
  static final float SQ6 = (float)Math.sqrt(6);
78
79 ee526fe0 Leszek Koltunski
  private static final float NODE_RATIO = 1.40f;
80
  private static final float MAX_SIZE_CHANGE = 1.35f;
81 81f4fd77 Leszek Koltunski
  private static final float MIN_SIZE_CHANGE = 0.75f;
82 c7b00dfb Leszek Koltunski
83 4c0a6d97 Leszek Koltunski
  private static final boolean mCreateFromDMesh = false;
84 19f0f767 Leszek Koltunski
85 8cccfb10 Leszek Koltunski
  private static final Static3D CENTER = new Static3D(0,0,0);
86 27e6c301 Leszek Koltunski
  private static final int POST_ROTATION_MILLISEC = 500;
87
88 efef689c Leszek Koltunski
  final Static3D[] ROTATION_AXIS;
89 98904e45 Leszek Koltunski
  final Static4D[] QUATS;
90 6b6504fe Leszek Koltunski
  final Cubit[] CUBITS;
91 470820a7 Leszek Koltunski
  final int NUM_FACES;
92 eab9d8f8 Leszek Koltunski
  final int NUM_TEXTURES;
93 8f53e513 Leszek Koltunski
  final int NUM_CUBIT_FACES;
94 1ebc4767 Leszek Koltunski
  final int NUM_AXIS;
95 6b6504fe Leszek Koltunski
  final int NUM_CUBITS;
96 a97e02b7 Leszek Koltunski
  final float[] CUTS;
97
  final int NUM_CUTS;
98 27a70eae Leszek Koltunski
99 b30695c6 Leszek Koltunski
  private static float mInitScreenRatio;
100
  private static float mObjectScreenRatio = 1.0f;
101 f0fa83ae Leszek Koltunski
102 5b893eee Leszek Koltunski
  private final int mNodeSize;
103 03aa05d5 Leszek Koltunski
  private final Static3D[] mOrigPos;
104
  private final Static3D mNodeScale;
105
  private final Static4D mQuat;
106
  private final int mNumLayers, mRealSize;
107
  private final ObjectList mList;
108
  private final DistortedEffects mEffects;
109
  private final VertexEffectRotate mRotateEffect;
110
  private final Dynamic1D mRotationAngle;
111
  private final Static3D mRotationAxis;
112
  private final Static3D mObjectScale;
113
  private final int[] mQuatDebug;
114 30bc2d91 Leszek Koltunski
  private final float mCameraDist;
115 03aa05d5 Leszek Koltunski
  private int mNumTexRows, mNumTexCols;
116 9224ffd2 Leszek Koltunski
  private int mRotRowBitmap;
117 efef689c Leszek Koltunski
  private int mRotAxis;
118 470820a7 Leszek Koltunski
  private MeshBase mMesh;
119 27a70eae Leszek Koltunski
120 7c969a6d Leszek Koltunski
  float[] mRowChances;
121 27a70eae Leszek Koltunski
  Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
122
  DistortedTexture mTexture;
123
  MatrixEffectScale mScaleEffect;
124 4da7d87a Leszek Koltunski
  MatrixEffectQuaternion mQuatEffect;
125 27a70eae Leszek Koltunski
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127 fdec60a3 Leszek Koltunski
128 db875721 Leszek Koltunski
  TwistyObject(int numLayers, int realSize, Static4D quat, DistortedTexture nodeTexture, MeshSquare nodeMesh,
129 9c2f0c91 Leszek Koltunski
               DistortedEffects nodeEffects, int[][] moves, ObjectList list, Resources res, int screenWidth)
130 fdec60a3 Leszek Koltunski
    {
131 411c6285 Leszek Koltunski
    super(nodeTexture,nodeEffects,nodeMesh);
132 fdec60a3 Leszek Koltunski
133 5b893eee Leszek Koltunski
    mNodeSize = screenWidth;
134
135 c7b00dfb Leszek Koltunski
    resizeFBO(mNodeSize, (int)(NODE_RATIO*mNodeSize));
136 d41742f7 Leszek Koltunski
137 d99f3a48 Leszek Koltunski
    mNumLayers = numLayers;
138
    mRealSize = realSize;
139 aa171dee Leszek Koltunski
    mList = list;
140 d99f3a48 Leszek Koltunski
    mOrigPos = getCubitPositions(mNumLayers);
141 10a2e360 Leszek Koltunski
142 98904e45 Leszek Koltunski
    QUATS = getQuats();
143 49f67f9b Leszek Koltunski
    NUM_CUBITS  = mOrigPos.length;
144 efef689c Leszek Koltunski
    ROTATION_AXIS = getRotationAxis();
145 1ebc4767 Leszek Koltunski
    NUM_AXIS = ROTATION_AXIS.length;
146 b30695c6 Leszek Koltunski
    mInitScreenRatio = getScreenRatio();
147 470820a7 Leszek Koltunski
    NUM_FACES = getNumFaces();
148 8f53e513 Leszek Koltunski
    NUM_CUBIT_FACES = getNumCubitFaces();
149 a64e07d0 Leszek Koltunski
    NUM_TEXTURES = getNumStickerTypes(mNumLayers)*NUM_FACES;
150 d99f3a48 Leszek Koltunski
    CUTS = getCuts(mNumLayers);
151 a97e02b7 Leszek Koltunski
    NUM_CUTS = CUTS.length;
152 a10ada2a Leszek Koltunski
153 a15078bb Leszek Koltunski
    mQuatDebug = new int[NUM_CUBITS];
154
155 b30695c6 Leszek Koltunski
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
156
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
157
158 c7b00dfb Leszek Koltunski
    mNodeScale= new Static3D(1,NODE_RATIO,1);
159 4da7d87a Leszek Koltunski
    mQuat = quat;
160 e844c116 Leszek Koltunski
161 a64e07d0 Leszek Koltunski
    mRowChances = getRowChances(mNumLayers);
162 7c969a6d Leszek Koltunski
163 27e6c301 Leszek Koltunski
    mRotationAngle= new Dynamic1D();
164
    mRotationAxis = new Static3D(1,0,0);
165 8cccfb10 Leszek Koltunski
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
166 27e6c301 Leszek Koltunski
167 27a70eae Leszek Koltunski
    mRotationAngleStatic = new Static1D(0);
168
    mRotationAngleMiddle = new Static1D(0);
169
    mRotationAngleFinal  = new Static1D(0);
170
171 d99f3a48 Leszek Koltunski
    float scale  = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mRealSize;
172 19f0f767 Leszek Koltunski
    mObjectScale = new Static3D(scale,scale,scale);
173 c7b00dfb Leszek Koltunski
    mScaleEffect = new MatrixEffectScale(mObjectScale);
174 4da7d87a Leszek Koltunski
    mQuatEffect  = new MatrixEffectQuaternion(quat, CENTER);
175 27a70eae Leszek Koltunski
176
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
177 411c6285 Leszek Koltunski
    nodeEffects.apply(nodeScaleEffect);
178 a10ada2a Leszek Koltunski
179 ae755eda Leszek Koltunski
    mNumTexCols = NUM_STICKERS_IN_ROW;
180
    mNumTexRows = (NUM_TEXTURES+1)/NUM_STICKERS_IN_ROW;
181
182
    if( mNumTexCols*mNumTexRows < NUM_TEXTURES+1 ) mNumTexRows++;
183
184 6b6504fe Leszek Koltunski
    CUBITS = new Cubit[NUM_CUBITS];
185 19f0f767 Leszek Koltunski
    createMeshAndCubits(list,res);
186 7381193e Leszek Koltunski
187 19f0f767 Leszek Koltunski
    mTexture = new DistortedTexture();
188 470820a7 Leszek Koltunski
    mEffects = new DistortedEffects();
189 10585385 Leszek Koltunski
190 98904e45 Leszek Koltunski
    int num_quats = QUATS.length;
191 10585385 Leszek Koltunski
    for(int q=0; q<num_quats; q++)
192
      {
193 98904e45 Leszek Koltunski
      VertexEffectQuaternion vq = new VertexEffectQuaternion(QUATS[q],CENTER);
194 10585385 Leszek Koltunski
      vq.setMeshAssociation(0,q);
195
      mEffects.apply(vq);
196
      }
197
198 27e6c301 Leszek Koltunski
    mEffects.apply(mRotateEffect);
199 4da7d87a Leszek Koltunski
    mEffects.apply(mQuatEffect);
200 470820a7 Leszek Koltunski
    mEffects.apply(mScaleEffect);
201
202 dfbb340a Leszek Koltunski
    // Now postprocessed effects (the glow when you solve an object) require component centers. In
203 b376bfd7 Leszek Koltunski
    // order for the effect to be in front of the object, we need to set the center to be behind it.
204 dfbb340a Leszek Koltunski
    getMesh().setComponentCenter(0,0,0,-0.1f);
205
206 470820a7 Leszek Koltunski
    attach( new DistortedNode(mTexture,mEffects,mMesh) );
207
208 aa171dee Leszek Koltunski
    setupPosition(moves);
209
210 30bc2d91 Leszek Koltunski
    float fov = list.getFOV();
211
    double halfFOV = fov * (Math.PI/360);
212
    mCameraDist = 0.5f*NODE_RATIO / (float)Math.tan(halfFOV);
213
214
    setProjection( fov, 0.1f);
215 27a70eae Leszek Koltunski
    }
216
217 19f0f767 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
218
219 9c2f0c91 Leszek Koltunski
  private void createMeshAndCubits(ObjectList list, Resources res)
220 19f0f767 Leszek Koltunski
    {
221
    if( mCreateFromDMesh )
222
      {
223 d99f3a48 Leszek Koltunski
      int sizeIndex = ObjectList.getSizeIndex(list.ordinal(),mNumLayers);
224 19f0f767 Leszek Koltunski
      int resourceID= list.getResourceIDs()[sizeIndex];
225
226
      InputStream is = res.openRawResource(resourceID);
227
      DataInputStream dos = new DataInputStream(is);
228
      mMesh = new MeshFile(dos);
229
230
      try
231
        {
232
        is.close();
233
        }
234
      catch(IOException e)
235
        {
236
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
237
        }
238
239
      for(int i=0; i<NUM_CUBITS; i++)
240
        {
241 6b6504fe Leszek Koltunski
        CUBITS[i] = new Cubit(this,mOrigPos[i]);
242
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
243 19f0f767 Leszek Koltunski
        }
244 eaee1ddc Leszek Koltunski
245
      if( shouldResetTextureMaps() ) resetAllTextureMaps();
246 19f0f767 Leszek Koltunski
      }
247
    else
248
      {
249
      MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
250
251
      for(int i=0; i<NUM_CUBITS; i++)
252
        {
253 6b6504fe Leszek Koltunski
        CUBITS[i] = new Cubit(this,mOrigPos[i]);
254 a64e07d0 Leszek Koltunski
        cubitMesh[i] = createCubitMesh(i,mNumLayers);
255 19f0f767 Leszek Koltunski
        cubitMesh[i].apply(new MatrixEffectMove(mOrigPos[i]),1,0);
256 6b6504fe Leszek Koltunski
        cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
257 19f0f767 Leszek Koltunski
        }
258
259
      mMesh = new MeshJoined(cubitMesh);
260
      resetAllTextureMaps();
261
      }
262
    }
263
264 c7b00dfb Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
265
266
  public void setObjectRatio(float sizeChange)
267
    {
268
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
269
270 b30695c6 Leszek Koltunski
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
271
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
272 c7b00dfb Leszek Koltunski
273 d99f3a48 Leszek Koltunski
    float scale = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mRealSize;
274 c7b00dfb Leszek Koltunski
    mObjectScale.set(scale,scale,scale);
275
    }
276
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
279
  static float getObjectRatio()
280
    {
281 b30695c6 Leszek Koltunski
    return mObjectScreenRatio*mInitScreenRatio;
282 c7b00dfb Leszek Koltunski
    }
283
284 e844c116 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
285
286 a97e02b7 Leszek Koltunski
  int computeRow(float x, float y, float z, int rotIndex)
287 e844c116 Leszek Koltunski
    {
288 a97e02b7 Leszek Koltunski
    Static3D axis = ROTATION_AXIS[rotIndex];
289
    float tmp = x*axis.get0() + y*axis.get1() + z*axis.get2();
290 e844c116 Leszek Koltunski
291 a97e02b7 Leszek Koltunski
    for(int i=0; i<NUM_CUTS; i++)
292 e844c116 Leszek Koltunski
      {
293 a97e02b7 Leszek Koltunski
      if( tmp<CUTS[i] ) return i;
294 e844c116 Leszek Koltunski
      }
295
296 a97e02b7 Leszek Koltunski
    return NUM_CUTS;
297 e844c116 Leszek Koltunski
    }
298
299 985f3dfa Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
300
301
  private boolean wasRotateApplied()
302
    {
303
    return mEffects.exists(mRotateEffect.getID());
304
    }
305
306 efef689c Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
307
308 9224ffd2 Leszek Koltunski
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
309 efef689c Leszek Koltunski
    {
310 221a4090 Leszek Koltunski
    return ((1<<CUBITS[cubit].mRotationRow[axis]) & rowBitmap) != 0;
311 66cbdd21 Leszek Koltunski
    }
312
313 aa171dee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
314 a31d25de Leszek Koltunski
// note the minus in front of the sin() - we rotate counterclockwise
315
// when looking towards the direction where the axis increases in values.
316 aa171dee Leszek Koltunski
317 a31d25de Leszek Koltunski
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
318 aa171dee Leszek Koltunski
    {
319 a31d25de Leszek Koltunski
    Static3D axis = ROTATION_AXIS[axisIndex];
320
321
    while( angleInDegrees<0 ) angleInDegrees += 360;
322
    angleInDegrees %= 360;
323
    
324
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
325
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
326
327
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
328
    }
329
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331
332 8bbac3c2 Leszek Koltunski
  private synchronized void setupPosition(int[][] moves)
333 a31d25de Leszek Koltunski
    {
334
    if( moves!=null )
335
      {
336
      Static4D quat;
337 818431ed Leszek Koltunski
      int index, axis, rowBitmap, angle;
338 a31d25de Leszek Koltunski
      int corr = (360/getBasicAngle());
339
340
      for(int[] move: moves)
341
        {
342
        axis     = move[0];
343
        rowBitmap= move[1];
344
        angle    = move[2]*corr;
345
        quat     = makeQuaternion(axis,angle);
346
347
        for(int j=0; j<NUM_CUBITS; j++)
348
          if( belongsToRotation(j,axis,rowBitmap) )
349
            {
350 6b6504fe Leszek Koltunski
            index = CUBITS[j].removeRotationNow(quat);
351
            mMesh.setEffectAssociation(j, CUBITS[j].computeAssociation(),index);
352 a31d25de Leszek Koltunski
            }
353
        }
354
      }
355 aa171dee Leszek Koltunski
    }
356
357 fa0f7a56 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
358
359
  int getCubitFaceColorIndex(int cubit, int face)
360
    {
361 470820a7 Leszek Koltunski
    Static4D texMap = mMesh.getTextureMap(NUM_FACES*cubit + face);
362 064ccc31 Leszek Koltunski
363
    int x = (int)(texMap.get0()/texMap.get2());
364
    int y = (int)(texMap.get1()/texMap.get3());
365
366
    return (mNumTexRows-1-y)*NUM_STICKERS_IN_ROW + x;
367 fa0f7a56 Leszek Koltunski
    }
368
369 49f67f9b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
370
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
371
372
  void clampPos(Static3D pos)
373
    {
374
    float currError, minError = Float.MAX_VALUE;
375
    int minErrorIndex= -1;
376
    float x = pos.get0();
377
    float y = pos.get1();
378
    float z = pos.get2();
379
    float xo,yo,zo;
380
381
    for(int i=0; i<NUM_CUBITS; i++)
382
      {
383
      xo = mOrigPos[i].get0();
384
      yo = mOrigPos[i].get1();
385
      zo = mOrigPos[i].get2();
386
387
      currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
388
389
      if( currError<minError )
390
        {
391
        minError = currError;
392
        minErrorIndex = i;
393
        }
394
      }
395
396
    pos.set( mOrigPos[minErrorIndex] );
397
    }
398
399 221a4090 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
400
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
401
// then if it were rotated by quaternion 'quat'.
402
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
403
// middle squares get interchanged. No visible difference!
404
//
405
// So: this is true iff the cubit
406
// a) is a corner or edge and the quaternions are the same
407
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
408
409 722b2512 Leszek Koltunski
  boolean thereIsVisibleDifference(Cubit cubit, int quatIndex)
410 221a4090 Leszek Koltunski
    {
411 722b2512 Leszek Koltunski
    if ( cubit.mQuatIndex == quatIndex ) return false;
412 221a4090 Leszek Koltunski
413
    int belongsToHowManyFaces = 0;
414
    int lastLayer = getNumLayers()-1;
415
    int row;
416
417
    for(int i=0; i<NUM_AXIS; i++)
418
      {
419
      row = cubit.mRotationRow[i];
420
      if( row==0 || row==lastLayer ) belongsToHowManyFaces++;
421
      }
422
423
    switch(belongsToHowManyFaces)
424
      {
425 722b2512 Leszek Koltunski
      case 0 : return false;  // 'inside' cubit that does not lie on any face
426 221a4090 Leszek Koltunski
      case 1 :                // cubit that lies inside one of the faces
427
               Static3D orig = cubit.getOrigPosition();
428
               Static4D quat1 = QUATS[quatIndex];
429
               Static4D quat2 = QUATS[cubit.mQuatIndex];
430
431
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
432
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
433
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
434
435
               float row1, row2;
436
               float x1 = rotated1.get0();
437
               float y1 = rotated1.get1();
438
               float z1 = rotated1.get2();
439
               float x2 = rotated2.get0();
440
               float y2 = rotated2.get1();
441
               float z2 = rotated2.get2();
442
443
               for(int i=0; i<NUM_AXIS; i++)
444
                 {
445
                 row1 = computeRow(x1,y1,z1,i);
446
                 row2 = computeRow(x2,y2,z2,i);
447
448 722b2512 Leszek Koltunski
                 if( (row1==0 && row2==0) || (row1==lastLayer && row2==lastLayer) ) return false;
449 221a4090 Leszek Koltunski
                 }
450 722b2512 Leszek Koltunski
               return true;
451 221a4090 Leszek Koltunski
452 722b2512 Leszek Koltunski
      default: return true;  // edge or corner
453 221a4090 Leszek Koltunski
      }
454
    }
455
456 411c6285 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
457 ae755eda Leszek Koltunski
// the getFaceColors + final black in a grid (so that we do not exceed the maximum texture size)
458 411c6285 Leszek Koltunski
459
  public void createTexture()
460
    {
461
    Bitmap bitmap;
462
463
    Paint paint = new Paint();
464 ae755eda Leszek Koltunski
    bitmap = Bitmap.createBitmap( mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
465 411c6285 Leszek Koltunski
    Canvas canvas = new Canvas(bitmap);
466
467
    paint.setAntiAlias(true);
468
    paint.setTextAlign(Paint.Align.CENTER);
469
    paint.setStyle(Paint.Style.FILL);
470
471 ee526fe0 Leszek Koltunski
    paint.setColor(COLOR_BLACK);
472 ae755eda Leszek Koltunski
    canvas.drawRect(0, 0, mNumTexCols*TEXTURE_HEIGHT, mNumTexRows*TEXTURE_HEIGHT, paint);
473 411c6285 Leszek Koltunski
474 ae755eda Leszek Koltunski
    int tex = 0;
475
476
    for(int row=0; row<mNumTexRows; row++)
477
      for(int col=0; col<mNumTexCols; col++)
478
        {
479
        if( tex>=NUM_TEXTURES ) break;
480
        createFaceTexture(canvas, paint, tex, col*TEXTURE_HEIGHT, row*TEXTURE_HEIGHT);
481
        tex++;
482
        }
483 411c6285 Leszek Koltunski
484 c7e23561 Leszek Koltunski
    if( !mTexture.setTexture(bitmap) )
485
      {
486
      int max = DistortedLibrary.getMaxTextureSize();
487
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
488
      crashlytics.log("failed to set texture of size "+bitmap.getWidth()+"x"+bitmap.getHeight()+" max is "+max);
489
      }
490 411c6285 Leszek Koltunski
    }
491
492 dd73fdab Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
493
494 d99f3a48 Leszek Koltunski
  public int getNumLayers()
495 fdec60a3 Leszek Koltunski
    {
496 d99f3a48 Leszek Koltunski
    return mNumLayers;
497 fdec60a3 Leszek Koltunski
    }
498
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500
501 27a70eae Leszek Koltunski
  public void continueRotation(float angleInDegrees)
502 fdec60a3 Leszek Koltunski
    {
503 27a70eae Leszek Koltunski
    mRotationAngleStatic.set0(angleInDegrees);
504 fdec60a3 Leszek Koltunski
    }
505
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507
508 27a70eae Leszek Koltunski
  public Static4D getRotationQuat()
509
      {
510 4da7d87a Leszek Koltunski
      return mQuat;
511 27a70eae Leszek Koltunski
      }
512
513
///////////////////////////////////////////////////////////////////////////////////////////////////
514
515 f18e8fae Leszek Koltunski
  public void recomputeScaleFactor(int scrWidth)
516 fdec60a3 Leszek Koltunski
    {
517 3717a94e Leszek Koltunski
    mNodeScale.set(scrWidth,NODE_RATIO*scrWidth,scrWidth);
518 fdec60a3 Leszek Koltunski
    }
519 27a70eae Leszek Koltunski
520
///////////////////////////////////////////////////////////////////////////////////////////////////
521
522 a10ada2a Leszek Koltunski
  public void savePreferences(SharedPreferences.Editor editor)
523
    {
524 6b6504fe Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++) CUBITS[i].savePreferences(editor);
525 a10ada2a Leszek Koltunski
    }
526 f16ff19d Leszek Koltunski
527 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
528 27a70eae Leszek Koltunski
529 8bbac3c2 Leszek Koltunski
  public synchronized void restorePreferences(SharedPreferences preferences)
530 a10ada2a Leszek Koltunski
    {
531 fc3c5170 Leszek Koltunski
    boolean error = false;
532
533 2fcad75d Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++)
534
      {
535 a15078bb Leszek Koltunski
      mQuatDebug[i] = CUBITS[i].restorePreferences(preferences);
536 1d6c1eea Leszek Koltunski
537 fc3c5170 Leszek Koltunski
      if( mQuatDebug[i]>=0 && mQuatDebug[i]<QUATS.length)
538 1d6c1eea Leszek Koltunski
        {
539 fc3c5170 Leszek Koltunski
        CUBITS[i].modifyCurrentPosition(QUATS[mQuatDebug[i]]);
540
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),mQuatDebug[i]);
541
        }
542
      else
543
        {
544
        error = true;
545 1d6c1eea Leszek Koltunski
        }
546 fc3c5170 Leszek Koltunski
      }
547 1d6c1eea Leszek Koltunski
548 fc3c5170 Leszek Koltunski
    if( error )
549
      {
550
      for(int i=0; i<NUM_CUBITS; i++)
551
        {
552
        CUBITS[i].solve();
553
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),0);
554
        }
555
      recordQuatsState("Failed to restorePreferences");
556 a15078bb Leszek Koltunski
      }
557
    }
558
559
///////////////////////////////////////////////////////////////////////////////////////////////////
560
561
  public void recordQuatsState(String message)
562
    {
563
    StringBuilder quats = new StringBuilder();
564
565
    for(int j=0; j<NUM_CUBITS; j++)
566
      {
567
      quats.append(mQuatDebug[j]);
568
      quats.append(" ");
569 2fcad75d Leszek Koltunski
      }
570 a15078bb Leszek Koltunski
571 25445dcf Leszek Koltunski
    if( BuildConfig.DEBUG )
572
      {
573 2d9d9d62 Leszek Koltunski
      android.util.Log.e("quats" , quats.toString());
574 25445dcf Leszek Koltunski
      android.util.Log.e("object", mList.name()+"_"+mNumLayers);
575
      }
576
    else
577
      {
578
      Exception ex = new Exception(message);
579
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
580
      crashlytics.setCustomKey("quats" , quats.toString());
581
      crashlytics.setCustomKey("object", mList.name()+"_"+mNumLayers );
582
      crashlytics.recordException(ex);
583
      }
584 a10ada2a Leszek Koltunski
    }
585 27a70eae Leszek Koltunski
586 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
587
588
  public void releaseResources()
589
    {
590
    mTexture.markForDeletion();
591 54342a21 Leszek Koltunski
    mMesh.markForDeletion();
592
    mEffects.markForDeletion();
593
594
    for(int j=0; j<NUM_CUBITS; j++)
595
      {
596
      CUBITS[j].releaseResources();
597
      }
598 a10ada2a Leszek Koltunski
    }
599
600
///////////////////////////////////////////////////////////////////////////////////////////////////
601
602
  public void apply(Effect effect, int position)
603
    {
604 8cccfb10 Leszek Koltunski
    mEffects.apply(effect, position);
605 a10ada2a Leszek Koltunski
    }
606
607
///////////////////////////////////////////////////////////////////////////////////////////////////
608
609
  public void remove(long effectID)
610
    {
611 8cccfb10 Leszek Koltunski
    mEffects.abortById(effectID);
612 a10ada2a Leszek Koltunski
    }
613 74686c71 Leszek Koltunski
614 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
615
616 8bbac3c2 Leszek Koltunski
  public synchronized void solve()
617 a10ada2a Leszek Koltunski
    {
618 98904e45 Leszek Koltunski
    for(int i=0; i<NUM_CUBITS; i++)
619
      {
620 6b6504fe Leszek Koltunski
      CUBITS[i].solve();
621
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
622 a10ada2a Leszek Koltunski
      }
623
    }
624
625 1f9772f3 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
626
627
  public void resetAllTextureMaps()
628
    {
629 ae755eda Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
630
    final float ratioH = 1.0f/mNumTexRows;
631
    int color, row, col;
632 380162cb Leszek Koltunski
633 ad73edd5 Leszek Koltunski
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
634 1f9772f3 Leszek Koltunski
      {
635 8f53e513 Leszek Koltunski
      final Static4D[] maps = new Static4D[NUM_CUBIT_FACES];
636 ad73edd5 Leszek Koltunski
637 f6d06256 Leszek Koltunski
      for(int cubitface=0; cubitface<NUM_CUBIT_FACES; cubitface++)
638 ad73edd5 Leszek Koltunski
        {
639 d99f3a48 Leszek Koltunski
        color = getFaceColor(cubit,cubitface,mNumLayers);
640 ae755eda Leszek Koltunski
        row = (mNumTexRows-1) - color/mNumTexCols;
641
        col = color%mNumTexCols;
642
        maps[cubitface] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
643 ad73edd5 Leszek Koltunski
        }
644
645 8f53e513 Leszek Koltunski
      mMesh.setTextureMap(maps,NUM_CUBIT_FACES*cubit);
646 1f9772f3 Leszek Koltunski
      }
647
    }
648
649
///////////////////////////////////////////////////////////////////////////////////////////////////
650
651
  public void setTextureMap(int cubit, int face, int newColor)
652
    {
653 064ccc31 Leszek Koltunski
    final float ratioW = 1.0f/mNumTexCols;
654
    final float ratioH = 1.0f/mNumTexRows;
655 8f53e513 Leszek Koltunski
    final Static4D[] maps = new Static4D[NUM_CUBIT_FACES];
656 064ccc31 Leszek Koltunski
    int row = (mNumTexRows-1) - newColor/mNumTexCols;
657
    int col = newColor%mNumTexCols;
658 1f9772f3 Leszek Koltunski
659 064ccc31 Leszek Koltunski
    maps[face] = new Static4D( col*ratioW, row*ratioH, ratioW, ratioH);
660 8f53e513 Leszek Koltunski
    mMesh.setTextureMap(maps,NUM_CUBIT_FACES*cubit);
661 1f9772f3 Leszek Koltunski
    }
662
663 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
664
665 8bbac3c2 Leszek Koltunski
  public synchronized void beginNewRotation(int axis, int row )
666 a10ada2a Leszek Koltunski
    {
667 9cd7695f Leszek Koltunski
    if( axis<0 || axis>=ROTATION_AXIS.length )
668
      {
669
      android.util.Log.e("object", "invalid rotation axis: "+axis);
670
      return;
671
      }
672 d99f3a48 Leszek Koltunski
    if( row<0 || row>=mNumLayers )
673 9cd7695f Leszek Koltunski
      {
674
      android.util.Log.e("object", "invalid rotation row: "+row);
675
      return;
676
      }
677
678 27e6c301 Leszek Koltunski
    mRotAxis     = axis;
679
    mRotRowBitmap= (1<<row);
680 a10ada2a Leszek Koltunski
    mRotationAngleStatic.set0(0.0f);
681 27e6c301 Leszek Koltunski
    mRotationAxis.set( ROTATION_AXIS[axis] );
682
    mRotationAngle.add(mRotationAngleStatic);
683 9c2f0c91 Leszek Koltunski
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis* ObjectList.MAX_OBJECT_SIZE) , -1);
684 27e6c301 Leszek Koltunski
    }
685 a10ada2a Leszek Koltunski
686
///////////////////////////////////////////////////////////////////////////////////////////////////
687
688 8bbac3c2 Leszek Koltunski
  public synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
689 27e6c301 Leszek Koltunski
    {
690 985f3dfa Leszek Koltunski
    if( wasRotateApplied() )
691
      {
692
      mRotAxis     = axis;
693
      mRotRowBitmap= rowBitmap;
694
695
      mRotationAngleStatic.set0(0.0f);
696
      mRotationAxis.set( ROTATION_AXIS[axis] );
697
      mRotationAngle.setDuration(durationMillis);
698
      mRotationAngle.resetToBeginning();
699
      mRotationAngle.add(new Static1D(0));
700
      mRotationAngle.add(new Static1D(angle));
701
      mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis* ObjectList.MAX_OBJECT_SIZE) , -1);
702
      mRotateEffect.notifyWhenFinished(listener);
703
704
      return mRotateEffect.getID();
705
      }
706 27e6c301 Leszek Koltunski
707 985f3dfa Leszek Koltunski
    return 0;
708 27e6c301 Leszek Koltunski
    }
709 a10ada2a Leszek Koltunski
710 27e6c301 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
711 a10ada2a Leszek Koltunski
712 168b6b56 Leszek Koltunski
  public long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
713 27e6c301 Leszek Koltunski
    {
714 985f3dfa Leszek Koltunski
    if( wasRotateApplied() )
715
      {
716
      float angle = getAngle();
717
      mRotationAngleStatic.set0(angle);
718
      mRotationAngleFinal.set0(nearestAngleInDegrees);
719
      mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
720
721
      mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
722
      mRotationAngle.resetToBeginning();
723
      mRotationAngle.removeAll();
724
      mRotationAngle.add(mRotationAngleStatic);
725
      mRotationAngle.add(mRotationAngleMiddle);
726
      mRotationAngle.add(mRotationAngleFinal);
727
      mRotateEffect.notifyWhenFinished(listener);
728
729
      return mRotateEffect.getID();
730
      }
731 27e6c301 Leszek Koltunski
732 985f3dfa Leszek Koltunski
    return 0;
733 27e6c301 Leszek Koltunski
    }
734 001cc0e4 Leszek Koltunski
735
///////////////////////////////////////////////////////////////////////////////////////////////////
736
737 27e6c301 Leszek Koltunski
  private float getAngle()
738 001cc0e4 Leszek Koltunski
    {
739 27e6c301 Leszek Koltunski
    int pointNum = mRotationAngle.getNumPoints();
740 001cc0e4 Leszek Koltunski
741 27e6c301 Leszek Koltunski
    if( pointNum>=1 )
742 001cc0e4 Leszek Koltunski
      {
743 27e6c301 Leszek Koltunski
      return mRotationAngle.getPoint(pointNum-1).get0();
744
      }
745
    else
746
      {
747
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
748
      crashlytics.log("points in RotationAngle: "+pointNum);
749
      return 0;
750 001cc0e4 Leszek Koltunski
      }
751
    }
752
753 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
754
755 8bbac3c2 Leszek Koltunski
  public synchronized void removeRotationNow()
756 168b6b56 Leszek Koltunski
    {
757
    float angle = getAngle();
758
    double nearestAngleInRadians = angle*Math.PI/180;
759
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
760
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
761
    float axisX = ROTATION_AXIS[mRotAxis].get0();
762
    float axisY = ROTATION_AXIS[mRotAxis].get1();
763
    float axisZ = ROTATION_AXIS[mRotAxis].get2();
764
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
765
766
    mRotationAngle.removeAll();
767
    mRotationAngleStatic.set0(0);
768
769
    for(int i=0; i<NUM_CUBITS; i++)
770
      if( belongsToRotation(i,mRotAxis,mRotRowBitmap) )
771
        {
772 6b6504fe Leszek Koltunski
        int index = CUBITS[i].removeRotationNow(quat);
773
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
774 168b6b56 Leszek Koltunski
        }
775
    }
776 a10ada2a Leszek Koltunski
777 aa171dee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
778
779 a31d25de Leszek Koltunski
  public void initializeObject(int[][] moves)
780 aa171dee Leszek Koltunski
    {
781
    solve();
782
    setupPosition(moves);
783
    }
784
785 9621255f Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
786
787
  public int getCubit(float[] point3D)
788
    {
789 418aa554 Leszek Koltunski
    float dist, minDist = Float.MAX_VALUE;
790 9621255f Leszek Koltunski
    int currentBest=-1;
791
    float multiplier = returnMultiplier();
792
793
    point3D[0] *= multiplier;
794
    point3D[1] *= multiplier;
795
    point3D[2] *= multiplier;
796
797
    for(int i=0; i<NUM_CUBITS; i++)
798
      {
799 6b6504fe Leszek Koltunski
      dist = CUBITS[i].getDistSquared(point3D);
800 9621255f Leszek Koltunski
      if( dist<minDist )
801
        {
802
        minDist = dist;
803
        currentBest = i;
804
        }
805
      }
806
807
    return currentBest;
808
    }
809
810 0e5ad27c Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
811
812 168b6b56 Leszek Koltunski
  public int computeNearestAngle(float angle, float speed)
813 0e5ad27c Leszek Koltunski
    {
814
    final int NEAREST = 360/getBasicAngle();
815
816 4c864c68 Leszek Koltunski
    int tmp = (int)((angle+NEAREST/2)/NEAREST);
817
    if( angle< -(NEAREST*0.5) ) tmp-=1;
818 168b6b56 Leszek Koltunski
819 4c864c68 Leszek Koltunski
    if( tmp!=0 ) return NEAREST*tmp;
820 168b6b56 Leszek Koltunski
821 c7b00dfb Leszek Koltunski
    return speed> 1.2f ? NEAREST*(angle>0 ? 1:-1) : 0;
822 0e5ad27c Leszek Koltunski
    }
823
824 30bc2d91 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
825
826
  public float getCameraDist()
827
    {
828
    return mCameraDist;
829
    }
830
831 5b893eee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
832
833
  public int getNodeSize()
834
    {
835
    return mNodeSize;
836
    }
837
838 aa171dee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
839
840 9c2f0c91 Leszek Koltunski
  public ObjectList getObjectList()
841 aa171dee Leszek Koltunski
    {
842
    return mList;
843
    }
844
845 10a2e360 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
846
847 f0fa83ae Leszek Koltunski
  abstract float getScreenRatio();
848 ae755eda Leszek Koltunski
  abstract Static3D[] getCubitPositions(int numLayers);
849 10585385 Leszek Koltunski
  abstract Static4D[] getQuats();
850 411c6285 Leszek Koltunski
  abstract int getNumFaces();
851 a64e07d0 Leszek Koltunski
  abstract int getNumStickerTypes(int numLayers);
852 8f53e513 Leszek Koltunski
  abstract int getNumCubitFaces();
853 a64e07d0 Leszek Koltunski
  abstract MeshBase createCubitMesh(int cubit, int numLayers);
854 ae755eda Leszek Koltunski
  abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top);
855
  abstract int getFaceColor(int cubit, int cubitface, int numLayers);
856 fb377dae Leszek Koltunski
  abstract float returnMultiplier();
857 a64e07d0 Leszek Koltunski
  abstract float[] getRowChances(int numLayers);
858 ae755eda Leszek Koltunski
  abstract float[] getCuts(int numLayers);
859 eaee1ddc Leszek Koltunski
  abstract boolean shouldResetTextureMaps();
860 7c969a6d Leszek Koltunski
861 6b6504fe Leszek Koltunski
  public abstract boolean isSolved();
862 12ad3fca Leszek Koltunski
  public abstract Static3D[] getRotationAxis();
863 e844c116 Leszek Koltunski
  public abstract int getBasicAngle();
864 20931cf6 Leszek Koltunski
  public abstract String retObjectString();
865 7c969a6d Leszek Koltunski
  public abstract int randomizeNewRotAxis(Random rnd, int oldRotAxis);
866
  public abstract int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis);
867 6fd4a72c Leszek Koltunski
  public abstract int getObjectName(int numLayers);
868
  public abstract int getInventor(int numLayers);
869
  public abstract int getComplexity(int numLayers);
870 fdec60a3 Leszek Koltunski
  }