Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / RubikCube.java @ beb325a0

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

    
20
package org.distorted.object;
21

    
22
import android.graphics.Bitmap;
23
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import org.distorted.library.effect.Effect;
27
import org.distorted.library.effect.MatrixEffectMove;
28
import org.distorted.library.effect.MatrixEffectQuaternion;
29
import org.distorted.library.effect.MatrixEffectRotate;
30
import org.distorted.library.effect.MatrixEffectScale;
31
import org.distorted.library.effect.VertexEffectSink;
32
import org.distorted.library.main.DistortedEffects;
33
import org.distorted.library.main.DistortedNode;
34
import org.distorted.library.main.DistortedTexture;
35
import org.distorted.library.mesh.MeshCubes;
36
import org.distorted.library.mesh.MeshFlat;
37
import org.distorted.library.message.EffectListener;
38
import org.distorted.library.type.Dynamic1D;
39
import org.distorted.library.type.Static1D;
40
import org.distorted.library.type.Static3D;
41
import org.distorted.library.type.Static4D;
42
import org.distorted.magic.RubikSurfaceView;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

    
46
public class RubikCube extends DistortedNode
47
{
48
    private static final float CUBE_SCREEN_RATIO = 0.5f;
49
    private static final int POST_ROTATION_MILLISEC = 500;
50
    private static final int TEXTURE_SIZE = 100;
51

    
52
    private static final Static3D VectX = new Static3D(1,0,0);
53
    private static final Static3D VectY = new Static3D(0,1,0);
54
    private static final Static3D VectZ = new Static3D(0,0,1);
55

    
56
    public static final int VECTX = 0;  //
57
    public static final int VECTY = 1;  // don't change this
58
    public static final int VECTZ = 2;  //
59

    
60
    private DistortedNode[][][] mNodes;
61
    private MeshCubes[][][] mCubes;
62
    private DistortedEffects[][][] mEffects;
63
    private Static4D[][][] mQuatScramble;
64
    private Static3D[][][] mRotationAxis;
65
    private Dynamic1D[][][] mRotationAngle;
66
    private Static3D[][][] mCurrentPosition;
67
    private MatrixEffectRotate[][][] mRotateEffect;
68
    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
69
    private Static3D mMove, mScale, mNodeMove, mNodeScale;
70
    private Static4D mQuatAccumulated;
71
    private DistortedTexture mTexture;
72

    
73
    private int mRotAxis, mRotRow;
74
    private int mSize;
75

    
76
    private DistortedTexture mNodeTexture;
77
    private float mCubeSizeInScreenSpace;
78

    
79
///////////////////////////////////////////////////////////////////////////////////////////////////
80

    
81
    private int computeNearestAngle(float angle)
82
      {
83
      final int NEAREST = 90;
84

    
85
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
86
      if( angle< -(NEAREST*0.5) ) tmp-=1;
87

    
88
      return NEAREST*tmp;
89
      }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92
// All legal rotation quats must have all four of their components equal to either
93
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
94
//
95
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
96
// correct the value of the 'scramble' quat to what it should be.
97
//
98
// We also have to remember that the group of unit quaternions is a double-cover of rotations
99
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
100

    
101
    private static final float SQ2 = 0.5f*((float)Math.sqrt(2));
102
    private static final float[] LEGAL = { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 };
103

    
104
    private void normalizeScrambleQuat(int i, int j, int k)
105
      {
106
      Static4D quat = mQuatScramble[i][j][k];
107

    
108
      float x = quat.get1();
109
      float y = quat.get2();
110
      float z = quat.get3();
111
      float w = quat.get4();
112
      float diff;
113

    
114
      for(float legal: LEGAL)
115
        {
116
        diff = x-legal;
117
        if( diff*diff<0.01f ) x = legal;
118
        diff = y-legal;
119
        if( diff*diff<0.01f ) y = legal;
120
        diff = z-legal;
121
        if( diff*diff<0.01f ) z = legal;
122
        diff = w-legal;
123
        if( diff*diff<0.01f ) w = legal;
124
        }
125

    
126
      if( w<0 )
127
        {
128
        w = -w;
129
        z = -z;
130
        y = -y;
131
        x = -x;
132
        }
133
      else if( w==0 )
134
        {
135
        if( z<0 )
136
          {
137
          z = -z;
138
          y = -y;
139
          x = -x;
140
          }
141
        else if( z==0 )
142
          {
143
          if( y<0 )
144
            {
145
            y = -y;
146
            x = -x;
147
            }
148
          else if( y==0 )
149
            {
150
            if( x<0 )
151
              {
152
              x = -x;
153
              }
154
            }
155
          }
156
        }
157

    
158
      mQuatScramble[i][j][k].set(x,y,z,w);
159
      }
160

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

    
163
    private float getSinkStrength()
164
      {
165
      switch(mSize)
166
        {
167
        case 1 : return 1.1f;
168
        case 2 : return 1.5f;
169
        case 3 : return 1.8f;
170
        case 4 : return 2.0f;
171
        default: return 3.0f - 4.0f/mSize;
172
        }
173
      }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

    
177
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
178
      {
179
      switch(vector)
180
        {
181
        case VECTX: return mCurrentPosition[x][y][z].get1()==row;
182
        case VECTY: return mCurrentPosition[x][y][z].get2()==row;
183
        case VECTZ: return mCurrentPosition[x][y][z].get3()==row;
184
        }
185

    
186
      return false;
187
      }
188

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

    
191
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
192
      {
193
      Static3D current = mCurrentPosition[x][y][z];
194
      float diff = 0.5f*(mSize-1);
195
      float cubitCenterX = current.get1() - diff;
196
      float cubitCenterY = current.get2() - diff;
197
      float cubitCenterZ = current.get3() - diff;
198

    
199
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
200
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
201

    
202
      float rotatedX = rotatedCenter.get1() + diff;
203
      float rotatedY = rotatedCenter.get2() + diff;
204
      float rotatedZ = rotatedCenter.get3() + diff;
205

    
206
      int roundedX = (int)(rotatedX+0.1f);
207
      int roundedY = (int)(rotatedY+0.1f);
208
      int roundedZ = (int)(rotatedZ+0.1f);
209

    
210
      mCurrentPosition[x][y][z].set1(roundedX);
211
      mCurrentPosition[x][y][z].set2(roundedY);
212
      mCurrentPosition[x][y][z].set3(roundedZ);
213
      }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216

    
217
    void addNewRotation(int vector, int row )
218
      {
219
      Static3D axis = VectX;
220

    
221
      switch(vector)
222
        {
223
        case VECTX: axis = VectX; break;
224
        case VECTY: axis = VectY; break;
225
        case VECTZ: axis = VectZ; break;
226
        }
227

    
228
      mRotAxis = vector;
229
      mRotRow  = row;
230

    
231
      mRotationAngleStatic.set1(0.0f);
232

    
233
      for(int x=0; x<mSize; x++)
234
        for(int y=0; y<mSize; y++)
235
          for(int z=0; z<mSize; z++)
236
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
237
              {
238
              if( belongsToRotation(x,y,z,vector,mRotRow) )
239
                {
240
                mRotationAxis[x][y][z].set(axis);
241
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
242
                }
243
              }
244
      }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
    void continueRotation(float angleInDegrees)
249
      {
250
      mRotationAngleStatic.set1(angleInDegrees);
251
      }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254

    
255
    float returnCubeSizeInScreenSpace()
256
      {
257
      return mCubeSizeInScreenSpace;
258
      }
259

    
260
///////////////////////////////////////////////////////////////////////////////////////////////////
261
// PUBLIC API
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263

    
264
    public RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshFlat mesh, DistortedEffects effects)
265
      {
266
      super(texture,effects,mesh);
267

    
268
      mNodeTexture = texture;
269

    
270
      mSize = size;
271

    
272
      mRotationAngleStatic = new Static1D(0);
273
      mRotationAngleMiddle = new Static1D(0);
274
      mRotationAngleFinal  = new Static1D(0);
275

    
276
      mMove     = new Static3D(0,0,0);
277
      mScale    = new Static3D(1,1,1);
278
      mNodeMove = new Static3D(0,0,0);
279
      mNodeScale= new Static3D(1,1,1);
280

    
281
      mQuatAccumulated = quatAcc;
282

    
283
      mRotAxis = VECTX;
284
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
285

    
286
      mNodes          = new DistortedNode[mSize][mSize][mSize];
287
      mCubes          = new MeshCubes[mSize][mSize][mSize];
288
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
289
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
290
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
291
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
292
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
293
      mRotateEffect   = new MatrixEffectRotate[mSize][mSize][mSize];
294

    
295
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
296

    
297
      Static3D sinkCenter = new Static3D(TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f);
298
      Static3D matrCenter = new Static3D(0,0,0);
299
      Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
300

    
301
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), sinkCenter, region );
302
      MatrixEffectMove        moveEffect = new MatrixEffectMove(mMove);
303
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(mScale);
304
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatCur, matrCenter);
305
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatAcc, matrCenter);
306

    
307
      MatrixEffectMove       nodeMoveEffect  = new MatrixEffectMove(mNodeMove);
308
      MatrixEffectScale      nodeScaleEffect = new MatrixEffectScale(mNodeScale);
309

    
310
      effects.apply(nodeScaleEffect);
311
      effects.apply(nodeMoveEffect);
312

    
313
      // 3x2 bitmap = 6 squares:
314
      //
315
      // RED     GREEN   BLUE
316
      // YELLOW  WHITE   BROWN
317

    
318
      final float ze = 0.0f;
319
      final float ot = 1.0f/3.0f;
320
      final float tt = 2.0f/3.0f;
321
      final float oh = 1.0f/2.0f;
322
      final float of = 1.0f/40.0f;
323

    
324
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
325
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
326
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
327
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
328
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
329
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
330

    
331
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
332

    
333
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
334
      float nc = 0.5f*mSize;
335
      int vertices = (int)(24.0f/mSize + 2.0f);
336

    
337
      for(int x = 0; x< mSize; x++)
338
        for(int y = 0; y< mSize; y++)
339
          for(int z = 0; z< mSize; z++)
340
            {
341
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
342
              {
343
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
344
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
345
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
346
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
347
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
348
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
349

    
350
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
351
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
352
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
353
              mRotationAngle[x][y][z]   = new Dynamic1D();
354
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
355
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
356
              mRotateEffect[x][y][z]    = new MatrixEffectRotate(mRotationAngle[x][y][z], mRotationAxis[x][y][z], matrCenter);
357

    
358
              mEffects[x][y][z] = new DistortedEffects();
359
              mEffects[x][y][z].apply(sinkEffect);
360
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
361
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], matrCenter));
362
              mEffects[x][y][z].apply(mRotateEffect[x][y][z]);
363
              mEffects[x][y][z].apply(quatAEffect);
364
              mEffects[x][y][z].apply(quatCEffect);
365
              mEffects[x][y][z].apply(scaleEffect);
366
              mEffects[x][y][z].apply(moveEffect);
367

    
368
              mNodes[x][y][z] = new DistortedNode(mTexture,mEffects[x][y][z],mCubes[x][y][z]);
369

    
370
              attach(mNodes[x][y][z]);
371
              }
372
            }
373
      }
374

    
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376

    
377
    public Static4D getRotationQuat()
378
      {
379
      return mQuatAccumulated;
380
      }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
    public long finishRotationNow(EffectListener listener)
385
      {
386
      boolean first = true;
387
      long effectID=0;
388

    
389
      for(int x=0; x<mSize; x++)
390
        for(int y=0; y<mSize; y++)
391
          for(int z=0; z<mSize; z++)
392
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
393
              {
394
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
395
                {
396
                if( first )
397
                  {
398
                  first = false;
399
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
400
                  effectID = mRotateEffect[x][y][z].getID();
401
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
402

    
403
                  if( pointNum>=1 )
404
                    {
405
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
406
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
407
                    mRotationAngleStatic.set1(startingAngle);
408
                    mRotationAngleFinal.set1(nearestAngleInDegrees);
409
                    mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
410
                    }
411
                  else
412
                    {
413
                    android.util.Log.e("cube", "ERROR finishing rotation!");
414
                    return 0;
415
                    }
416
                  }
417

    
418
                mRotationAngle[x][y][z].setDuration(POST_ROTATION_MILLISEC);
419
                mRotationAngle[x][y][z].resetToBeginning();
420
                mRotationAngle[x][y][z].removeAll();
421
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
422
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
423
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
424
                }
425
              }
426

    
427
      return effectID;
428
      }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
432
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
433
// memory leak.
434

    
435
    public void releaseResources()
436
      {
437
      mTexture.markForDeletion();
438

    
439
      for(int x=0; x<mSize; x++)
440
        for(int y=0; y<mSize; y++)
441
          for(int z=0; z<mSize; z++)
442
            {
443
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
444
              {
445
              mCubes[x][y][z].markForDeletion();
446
              mNodes[x][y][z].markForDeletion();
447
              }
448
            }
449
      }
450

    
451
///////////////////////////////////////////////////////////////////////////////////////////////////
452

    
453
    public void createTexture()
454
      {
455
      Bitmap bitmap;
456

    
457
      final int S = 128;
458
      final int W = 3*S;
459
      final int H = 2*S;
460
      final int R = S/10;
461
      final int M = S/20;
462

    
463
      Paint paint = new Paint();
464
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
465
      Canvas canvas = new Canvas(bitmap);
466

    
467
      paint.setAntiAlias(true);
468
      paint.setTextAlign(Paint.Align.CENTER);
469
      paint.setStyle(Paint.Style.FILL);
470

    
471
      // 3x2 bitmap = 6 squares:
472
      //
473
      // RED     GREEN   BLUE
474
      // YELLOW  WHITE   BROWN
475

    
476
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
477
      canvas.drawRect(0, 0, W, H, paint);                          //
478

    
479
      paint.setColor(0xffff0000);                                  // RED
480
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
481
      paint.setColor(0xff00ff00);                                  // GREEN
482
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
483
      paint.setColor(0xff0000ff);                                  // BLUE
484
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
485
      paint.setColor(0xffffff00);                                  // YELLOW
486
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
487
      paint.setColor(0xffffffff);                                  // WHITE
488
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
489
      paint.setColor(0xffb5651d);                                  // BROWN
490
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
491

    
492
      mTexture.setTexture(bitmap);
493
      }
494

    
495
///////////////////////////////////////////////////////////////////////////////////////////////////
496

    
497
    public void recomputeScaleFactor(int screenWidth, int screenHeight)
498
      {
499
      mCubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
500

    
501
      int texW = mNodeTexture.getWidth();
502
      int texH = mNodeTexture.getHeight();
503

    
504
      if( (float)texH/texW > (float)screenHeight/screenWidth )
505
        {
506
        int w = (screenHeight*texW)/texH;
507
        float factor = (float)screenHeight/texH;
508
        mNodeMove.set((screenWidth-w)*0.5f ,0, 0);
509
        mNodeScale.set(factor,factor,factor);
510
        }
511
      else
512
        {
513
        int h = (screenWidth*texH)/texW;
514
        float factor = (float)screenWidth/texW;
515
        mNodeMove.set(0,(screenHeight-h)*0.5f,0);
516
        mNodeScale.set(factor,factor,factor);
517
        }
518

    
519
      float scaleFactor = (mCubeSizeInScreenSpace/(TEXTURE_SIZE*mSize)) * (float)texW/(screenWidth>screenHeight ? screenHeight:screenWidth);
520

    
521
      mMove.set( texW*0.5f , texH*0.5f , 0.0f );
522
      mScale.set(scaleFactor,scaleFactor,scaleFactor);
523
      }
524

    
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526

    
527
    public void apply(Effect effect, int position)
528
      {
529
      for(int x=0; x<mSize; x++)
530
        for(int y=0; y<mSize; y++)
531
          for(int z=0; z<mSize; z++)
532
            {
533
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
534
              {
535
              mEffects[x][y][z].apply(effect, position);
536
              }
537
            }
538
      }
539

    
540
///////////////////////////////////////////////////////////////////////////////////////////////////
541

    
542
    public void remove(long effectID)
543
      {
544
      for(int x=0; x<mSize; x++)
545
        for(int y=0; y<mSize; y++)
546
          for(int z=0; z<mSize; z++)
547
            {
548
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
549
              {
550
              mEffects[x][y][z].abortById(effectID);
551
              }
552
            }
553
      }
554

    
555
///////////////////////////////////////////////////////////////////////////////////////////////////
556

    
557
    public void solve()
558
      {
559
      for(int x=0; x<mSize; x++)
560
        for(int y=0; y<mSize; y++)
561
          for(int z=0; z<mSize; z++)
562
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
563
              {
564
              mQuatScramble[x][y][z].set(0,0,0,1);
565
              mCurrentPosition[x][y][z].set(x,y,z);
566
              }
567
      }
568

    
569
///////////////////////////////////////////////////////////////////////////////////////////////////
570

    
571
    public boolean isSolved()
572
      {
573
      Static4D q = mQuatScramble[0][0][0];
574

    
575
      float x = q.get1();
576
      float y = q.get2();
577
      float z = q.get3();
578
      float w = q.get4();
579

    
580
      for(int i = 0; i< mSize; i++)
581
        for(int j = 0; j< mSize; j++)
582
          for(int k = 0; k< mSize; k++)
583
            {
584
            if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 )
585
              {
586
              q = mQuatScramble[i][j][k];
587

    
588
              if( q.get1()!=x || q.get2()!=y || q.get3()!=z || q.get4()!=w )
589
                {
590
                return false;
591
                }
592
              }
593
            }
594

    
595
      return true;
596
      }
597

    
598
///////////////////////////////////////////////////////////////////////////////////////////////////
599

    
600
    public int getSize()
601
      {
602
      return mSize;
603
      }
604

    
605
///////////////////////////////////////////////////////////////////////////////////////////////////
606

    
607
    public long addNewRotation(int vector, int row, int angle, long durationMillis, EffectListener listener )
608
      {
609
      Static3D axis = VectX;
610
      long effectID=0;
611
      boolean first = true;
612

    
613
      switch(vector)
614
        {
615
        case VECTX: axis = VectX; break;
616
        case VECTY: axis = VectY; break;
617
        case VECTZ: axis = VectZ; break;
618
        }
619

    
620
      mRotAxis = vector;
621
      mRotRow  = row;
622

    
623
      mRotationAngleStatic.set1(0.0f);
624

    
625
      for(int x=0; x<mSize; x++)
626
        for(int y=0; y<mSize; y++)
627
          for(int z=0; z<mSize; z++)
628
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
629
              {
630
              if( belongsToRotation(x,y,z,vector,mRotRow) )
631
                {
632
                mRotationAxis[x][y][z].set(axis);
633
                mRotationAngle[x][y][z].setDuration(durationMillis);
634
                mRotationAngle[x][y][z].resetToBeginning();
635
                mRotationAngle[x][y][z].add(new Static1D(0));
636
                mRotationAngle[x][y][z].add(new Static1D(angle));
637

    
638
                if( first )
639
                  {
640
                  first = false;
641
                  effectID = mRotateEffect[x][y][z].getID();
642
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
643
                  }
644
                }
645
              }
646

    
647
      return effectID;
648
      }
649

    
650
///////////////////////////////////////////////////////////////////////////////////////////////////
651

    
652
    public void removeRotationNow()
653
      {
654
      float qx=0,qy=0,qz=0;
655
      boolean first = true;
656
      Static4D quat = null;
657

    
658
      switch(mRotAxis)
659
        {
660
        case VECTX: qx=1; break;
661
        case VECTY: qy=1; break;
662
        case VECTZ: qz=1; break;
663
        }
664

    
665
      for(int x=0; x<mSize; x++)
666
        for(int y=0; y<mSize; y++)
667
          for(int z=0; z<mSize; z++)
668
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
669
              {
670
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
671
                {
672
                if( first )
673
                  {
674
                  first = false;
675
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
676

    
677
                  if( pointNum>=1 )
678
                    {
679
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
680
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
681
                    double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
682
                    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
683
                    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
684
                    quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
685
                    }
686
                  else
687
                    {
688
                    android.util.Log.e("cube", "ERROR removing rotation!");
689
                    return;
690
                    }
691
                  }
692

    
693
                mRotationAngle[x][y][z].removeAll();
694
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
695
                normalizeScrambleQuat(x,y,z);
696
                modifyCurrentPosition(x,y,z,quat);
697
                }
698
              }
699

    
700
      mRotationAngleStatic.set1(0);
701
      }
702
}
(1-1/2)