Project

General

Profile

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

magiccube / src / main / java / org / distorted / magic / RubikCube.java @ 05fa94d9

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted 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
// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.magic;
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.EffectType;
28
import org.distorted.library.effect.MatrixEffectMove;
29
import org.distorted.library.effect.MatrixEffectQuaternion;
30
import org.distorted.library.effect.MatrixEffectRotate;
31
import org.distorted.library.effect.MatrixEffectScale;
32
import org.distorted.library.effect.VertexEffectSink;
33
import org.distorted.library.effectqueue.EffectQueue;
34
import org.distorted.library.main.DistortedEffects;
35
import org.distorted.library.main.DistortedNode;
36
import org.distorted.library.main.DistortedTexture;
37
import org.distorted.library.mesh.MeshCubes;
38
import org.distorted.library.mesh.MeshFlat;
39
import org.distorted.library.message.EffectListener;
40
import org.distorted.library.type.Dynamic1D;
41
import org.distorted.library.type.Static1D;
42
import org.distorted.library.type.Static3D;
43
import org.distorted.library.type.Static4D;
44

    
45
///////////////////////////////////////////////////////////////////////////////////////////////////
46

    
47
public class RubikCube extends DistortedNode
48
{
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
    private static final float SQ2 = 0.5f*((float)Math.sqrt(2));
57
    private static final float[] LEGAL = { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 };
58

    
59
    public static final int VECTX = 0;  //
60
    public static final int VECTY = 1;  // don't change this
61
    public static final int VECTZ = 2;  //
62

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

    
75
    private int mRotAxis, mRotRow;
76
    private int mSize;
77

    
78
    private DistortedTexture mNodeTexture;
79

    
80
///////////////////////////////////////////////////////////////////////////////////////////////////
81

    
82
    RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshFlat mesh, DistortedEffects effects)
83
      {
84
      super(texture,effects,mesh);
85

    
86
      mNodeTexture = texture;
87

    
88
      mSize = size;
89

    
90
      mRotationAngleStatic = new Static1D(0);
91
      mRotationAngleMiddle = new Static1D(0);
92
      mRotationAngleFinal  = new Static1D(0);
93

    
94
      mMove     = new Static3D(0,0,0);
95
      mScale    = new Static3D(1,1,1);
96
      mNodeMove = new Static3D(0,0,0);
97
      mNodeScale= new Static3D(1,1,1);
98

    
99
      mRotAxis = VECTX;
100
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
101

    
102
      mNodes          = new DistortedNode[mSize][mSize][mSize];
103
      mCubes          = new MeshCubes[mSize][mSize][mSize];
104
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
105
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
106
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
107
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
108
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
109
      mRotateEffect   = new MatrixEffectRotate[mSize][mSize][mSize];
110

    
111
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
112

    
113
      Static3D sinkCenter = new Static3D(TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f);
114
      Static3D matrCenter = new Static3D(0,0,0);
115
      Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
116

    
117
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), sinkCenter, region );
118
      MatrixEffectMove        moveEffect = new MatrixEffectMove(mMove);
119
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(mScale);
120
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatCur, matrCenter);
121
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatAcc, matrCenter);
122

    
123
      MatrixEffectMove       nodeMoveEffect  = new MatrixEffectMove(mNodeMove);
124
      MatrixEffectScale      nodeScaleEffect = new MatrixEffectScale(mNodeScale);
125

    
126
      effects.apply(nodeScaleEffect);
127
      effects.apply(nodeMoveEffect);
128

    
129
      // 3x2 bitmap = 6 squares:
130
      //
131
      // RED     GREEN   BLUE
132
      // YELLOW  WHITE   BROWN
133

    
134
      final float ze = 0.0f;
135
      final float ot = 1.0f/3.0f;
136
      final float tt = 2.0f/3.0f;
137
      final float oh = 1.0f/2.0f;
138
      final float of = 1.0f/40.0f;
139

    
140
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
141
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
142
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
143
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
144
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
145
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
146

    
147
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
148

    
149
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
150
      float nc = 0.5f*mSize;
151
      int vertices = (int)(24.0f/mSize + 2.0f);
152

    
153
      for(int x = 0; x< mSize; x++)
154
        for(int y = 0; y< mSize; y++)
155
          for(int z = 0; z< mSize; z++)
156
            {
157
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
158
              {
159
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
160
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
161
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
162
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
163
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
164
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
165

    
166
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
167
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
168
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
169
              mRotationAngle[x][y][z]   = new Dynamic1D();
170
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
171
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
172
              mRotateEffect[x][y][z]    = new MatrixEffectRotate(mRotationAngle[x][y][z], mRotationAxis[x][y][z], matrCenter);
173

    
174
              mEffects[x][y][z] = new DistortedEffects();
175
              mEffects[x][y][z].apply(sinkEffect);
176
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
177
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], matrCenter));
178
              mEffects[x][y][z].apply(mRotateEffect[x][y][z]);
179
              mEffects[x][y][z].apply(quatAEffect);
180
              mEffects[x][y][z].apply(quatCEffect);
181
              mEffects[x][y][z].apply(scaleEffect);
182
              mEffects[x][y][z].apply(moveEffect);
183

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

    
186
              attach(mNodes[x][y][z]);
187
              }
188
            }
189
      }
190

    
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192

    
193
    boolean isSolved()
194
      {
195
      Static4D q = mQuatScramble[0][0][0];
196

    
197
      float x = q.get1();
198
      float y = q.get2();
199
      float z = q.get3();
200
      float w = q.get4();
201

    
202
      for(int i = 0; i< mSize; i++)
203
        for(int j = 0; j< mSize; j++)
204
          for(int k = 0; k< mSize; k++)
205
            {
206
            if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 )
207
              {
208
              q = mQuatScramble[i][j][k];
209

    
210
              if( q.get1()!=x || q.get2()!=y || q.get3()!=z || q.get4()!=w )
211
                {
212
                return false;
213
                }
214
              }
215
            }
216

    
217
      return true;
218
      }
219

    
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
    public void apply(Effect effect, int position)
223
      {
224
      for(int x=0; x<mSize; x++)
225
        for(int y=0; y<mSize; y++)
226
          for(int z=0; z<mSize; z++)
227
            {
228
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
229
              {
230
              mEffects[x][y][z].apply(effect, position);
231
              }
232
            }
233
      }
234

    
235
///////////////////////////////////////////////////////////////////////////////////////////////////
236

    
237
    public void remove(long effectID)
238
      {
239
      for(int x=0; x<mSize; x++)
240
        for(int y=0; y<mSize; y++)
241
          for(int z=0; z<mSize; z++)
242
            {
243
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
244
              {
245
              mEffects[x][y][z].abortById(effectID);
246
              }
247
            }
248
      }
249

    
250
///////////////////////////////////////////////////////////////////////////////////////////////////
251

    
252
    public void solve()
253
      {
254
      for(int x=0; x<mSize; x++)
255
        for(int y=0; y<mSize; y++)
256
          for(int z=0; z<mSize; z++)
257
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
258
              {
259
              mQuatScramble[x][y][z].set(0,0,0,1);
260
              mCurrentPosition[x][y][z].set(x,y,z);
261
              }
262
      }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265

    
266
    public String print_effects()
267
      {
268
      String str="";
269

    
270
      EffectQueue[] effects = mEffects[0][0][0].getQueues();
271
      EffectQueue matrix      = effects[0];
272
      EffectQueue vertex      = effects[1];
273
      EffectQueue fragment    = effects[2];
274
      EffectQueue postprocess = effects[3];
275

    
276
      str+="MATRIX: ";
277
      int m_len = matrix.getNumEffects();
278
      for(int i=0; i<m_len; i++)
279
        {
280
        str+=(" "+matrix.getEffect(i).getName()+"("+matrix.getEffect(i).getID()+")" );
281
        }
282
      str+='\n';
283

    
284
      str+="VERTEX: ";
285
      int v_len = vertex.getNumEffects();
286
      for(int i=0; i<v_len; i++)
287
        {
288
        str+=(" "+vertex.getEffect(i).getName()+"("+matrix.getEffect(i).getID()+")" );
289
        }
290
      str+='\n';
291

    
292
      str+="FRAGMENT: ";
293
      int f_len = fragment.getNumEffects();
294
      for(int i=0; i<f_len; i++)
295
        {
296
        str+=(" "+fragment.getEffect(i).getName()+"("+matrix.getEffect(i).getID()+")" );
297
        }
298
      str+='\n';
299

    
300
      str+="POSTPROCESS: ";
301
      int p_len = postprocess.getNumEffects();
302
      for(int i=0; i<p_len; i++)
303
        {
304
        str+=(" "+postprocess.getEffect(i).getName()+"("+matrix.getEffect(i).getID()+")" );
305
        }
306
      str+='\n';
307

    
308
      return str;
309
      }
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

    
312
    public int getNumEffects(EffectType type)
313
      {
314
      return mEffects[0][0][0].getNumEffects(type);
315
      }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318

    
319
    public long addNewRotation(int vector, int row, int angle, long durationMillis, EffectListener listener )
320
      {
321
      Static3D axis = VectX;
322
      long effectID=0;
323
      boolean first = true;
324

    
325
      switch(vector)
326
        {
327
        case VECTX: axis = VectX; break;
328
        case VECTY: axis = VectY; break;
329
        case VECTZ: axis = VectZ; break;
330
        }
331

    
332
      mRotAxis = vector;
333
      mRotRow  = row;
334

    
335
      mRotationAngleStatic.set1(0.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
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
341
              {
342
              if( belongsToRotation(x,y,z,vector,mRotRow) )
343
                {
344
                mRotationAxis[x][y][z].set(axis);
345
                mRotationAngle[x][y][z].setDuration(durationMillis);
346
                mRotationAngle[x][y][z].resetToBeginning();
347
                mRotationAngle[x][y][z].add(new Static1D(0));
348
                mRotationAngle[x][y][z].add(new Static1D(angle));
349

    
350
                if( first )
351
                  {
352
                  first = false;
353
                  effectID = mRotateEffect[x][y][z].getID();
354
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
355
                  }
356
                }
357
              }
358

    
359
      return effectID;
360
      }
361

    
362
///////////////////////////////////////////////////////////////////////////////////////////////////
363

    
364
    public int getSize()
365
      {
366
      return mSize;
367
      }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
371
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
372
// memory leak.
373

    
374
    void releaseResources()
375
      {
376
      mTexture.markForDeletion();
377

    
378
      for(int x=0; x<mSize; x++)
379
        for(int y=0; y<mSize; y++)
380
          for(int z=0; z<mSize; z++)
381
            {
382
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
383
              {
384
              mCubes[x][y][z].markForDeletion();
385
              mNodes[x][y][z].markForDeletion();
386
              }
387
            }
388
      }
389

    
390
///////////////////////////////////////////////////////////////////////////////////////////////////
391

    
392
    void addNewRotation(int vector, int row )
393
      {
394
      Static3D axis = VectX;
395

    
396
      switch(vector)
397
        {
398
        case VECTX: axis = VectX; break;
399
        case VECTY: axis = VectY; break;
400
        case VECTZ: axis = VectZ; break;
401
        }
402

    
403
      mRotAxis = vector;
404
      mRotRow  = row;
405

    
406
      mRotationAngleStatic.set1(0.0f);
407

    
408
      for(int x=0; x<mSize; x++)
409
        for(int y=0; y<mSize; y++)
410
          for(int z=0; z<mSize; z++)
411
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
412
              {
413
              if( belongsToRotation(x,y,z,vector,mRotRow) )
414
                {
415
                mRotationAxis[x][y][z].set(axis);
416
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
417
                }
418
              }
419
      }
420

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

    
423
    void continueRotation(float angleInDegrees)
424
      {
425
      mRotationAngleStatic.set1(angleInDegrees);
426
      }
427

    
428
///////////////////////////////////////////////////////////////////////////////////////////////////
429

    
430
    private int computeNearestAngle(float angle)
431
      {
432
      final int NEAREST = 90;
433

    
434
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
435
      if( angle< -(NEAREST/2) ) tmp-=1;
436

    
437
      return NEAREST*tmp;
438
      }
439

    
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

    
442
    long finishRotationNow(EffectListener listener)
443
      {
444
      boolean first = true;
445
      long effectID=0;
446

    
447
      for(int x=0; x<mSize; x++)
448
        for(int y=0; y<mSize; y++)
449
          for(int z=0; z<mSize; z++)
450
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
451
              {
452
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
453
                {
454
                if( first )
455
                  {
456
                  first = false;
457
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
458
                  effectID = mRotateEffect[x][y][z].getID();
459
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
460

    
461
                  if( pointNum>=1 )
462
                    {
463
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
464
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
465
                    mRotationAngleStatic.set1(startingAngle);
466
                    mRotationAngleFinal.set1(nearestAngleInDegrees);
467
                    mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
468
                    }
469
                  else
470
                    {
471
                    android.util.Log.e("cube", "ERROR finishing rotation!");
472
                    return 0;
473
                    }
474
                  }
475

    
476
                mRotationAngle[x][y][z].setDuration(POST_ROTATION_MILLISEC);
477
                mRotationAngle[x][y][z].resetToBeginning();
478
                mRotationAngle[x][y][z].removeAll();
479
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
480
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
481
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
482
                }
483
              }
484

    
485
      return effectID;
486
      }
487

    
488
///////////////////////////////////////////////////////////////////////////////////////////////////
489

    
490
    public void removeRotationNow()
491
      {
492
      float qx=0,qy=0,qz=0;
493
      boolean first = true;
494
      Static4D quat = null;
495

    
496
      switch(mRotAxis)
497
        {
498
        case VECTX: qx=1; break;
499
        case VECTY: qy=1; break;
500
        case VECTZ: qz=1; break;
501
        }
502

    
503
      for(int x=0; x<mSize; x++)
504
        for(int y=0; y<mSize; y++)
505
          for(int z=0; z<mSize; z++)
506
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
507
              {
508
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
509
                {
510
                if( first )
511
                  {
512
                  first = false;
513
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
514

    
515
                  if( pointNum>=1 )
516
                    {
517
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
518
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
519
                    double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
520
                    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
521
                    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
522
                    quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
523
                    }
524
                  else
525
                    {
526
                    android.util.Log.e("cube", "ERROR removing rotation!");
527
                    return;
528
                    }
529
                  }
530

    
531
                mRotationAngle[x][y][z].removeAll();
532
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
533
                normalizeScrambleQuat(x,y,z);
534
                modifyCurrentPosition(x,y,z,quat);
535
                }
536
              }
537

    
538
      mRotationAngleStatic.set1(0);
539
      }
540

    
541
///////////////////////////////////////////////////////////////////////////////////////////////////
542
// All legal rotation quats must have all four of their components equal to either
543
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
544
//
545
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
546
// correct the value of the 'scramble' quat to what it should be.
547
//
548
// We also have to remember that the group of unit quaternions is a double-cover of rotations
549
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
550

    
551
    private void normalizeScrambleQuat(int i, int j, int k)
552
      {
553
      Static4D quat = mQuatScramble[i][j][k];
554

    
555
      float x = quat.get1();
556
      float y = quat.get2();
557
      float z = quat.get3();
558
      float w = quat.get4();
559
      float diff;
560

    
561
      for(int legal=0; legal<LEGAL.length; legal++)
562
        {
563
        diff = x-LEGAL[legal];
564
        if( diff*diff<0.01f ) x = LEGAL[legal];
565
        diff = y-LEGAL[legal];
566
        if( diff*diff<0.01f ) y = LEGAL[legal];
567
        diff = z-LEGAL[legal];
568
        if( diff*diff<0.01f ) z = LEGAL[legal];
569
        diff = w-LEGAL[legal];
570
        if( diff*diff<0.01f ) w = LEGAL[legal];
571
        }
572

    
573
      if( w<0 )
574
        {
575
        w = -w;
576
        z = -z;
577
        y = -y;
578
        x = -x;
579
        }
580
      else if( w==0 )
581
        {
582
        if( z<0 )
583
          {
584
          z = -z;
585
          y = -y;
586
          x = -x;
587
          }
588
        else if( z==0 )
589
          {
590
          if( y<0 )
591
            {
592
            y = -y;
593
            x = -x;
594
            }
595
          else if( y==0 )
596
            {
597
            if( x<0 )
598
              {
599
              x = -x;
600
              }
601
            }
602
          }
603
        }
604

    
605
      mQuatScramble[i][j][k].set(x,y,z,w);
606
      }
607

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

    
610
    private float getSinkStrength()
611
      {
612
      switch(mSize)
613
        {
614
        case 1 : return 1.1f;
615
        case 2 : return 1.5f;
616
        case 3 : return 1.8f;
617
        case 4 : return 2.0f;
618
        default: return 3.0f - 4.0f/mSize;
619
        }
620
      }
621

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

    
624
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
625
      {
626
      switch(vector)
627
        {
628
        case VECTX: return mCurrentPosition[x][y][z].get1()==row;
629
        case VECTY: return mCurrentPosition[x][y][z].get2()==row;
630
        case VECTZ: return mCurrentPosition[x][y][z].get3()==row;
631
        }
632

    
633
      return false;
634
      }
635

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

    
638
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
639
      {
640
      Static3D current = mCurrentPosition[x][y][z];
641
      float diff = 0.5f*(mSize-1);
642
      float cubitCenterX = current.get1() - diff;
643
      float cubitCenterY = current.get2() - diff;
644
      float cubitCenterZ = current.get3() - diff;
645

    
646
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
647
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
648

    
649
      float rotatedX = rotatedCenter.get1() + diff;
650
      float rotatedY = rotatedCenter.get2() + diff;
651
      float rotatedZ = rotatedCenter.get3() + diff;
652

    
653
      int roundedX = (int)(rotatedX+0.1f);
654
      int roundedY = (int)(rotatedY+0.1f);
655
      int roundedZ = (int)(rotatedZ+0.1f);
656

    
657
      mCurrentPosition[x][y][z].set1(roundedX);
658
      mCurrentPosition[x][y][z].set2(roundedY);
659
      mCurrentPosition[x][y][z].set3(roundedZ);
660
      }
661

    
662
///////////////////////////////////////////////////////////////////////////////////////////////////
663

    
664
    void createTexture()
665
      {
666
      Bitmap bitmap;
667

    
668
      final int S = 128;
669
      final int W = 3*S;
670
      final int H = 2*S;
671
      final int R = S/10;
672
      final int M = S/20;
673

    
674
      Paint paint = new Paint();
675
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
676
      Canvas canvas = new Canvas(bitmap);
677

    
678
      paint.setAntiAlias(true);
679
      paint.setTextAlign(Paint.Align.CENTER);
680
      paint.setStyle(Paint.Style.FILL);
681

    
682
      // 3x2 bitmap = 6 squares:
683
      //
684
      // RED     GREEN   BLUE
685
      // YELLOW  WHITE   BROWN
686

    
687
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
688
      canvas.drawRect(0, 0, W, H, paint);                          //
689

    
690
      paint.setColor(0xffff0000);                                  // RED
691
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
692
      paint.setColor(0xff00ff00);                                  // GREEN
693
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
694
      paint.setColor(0xff0000ff);                                  // BLUE
695
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
696
      paint.setColor(0xffffff00);                                  // YELLOW
697
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
698
      paint.setColor(0xffffffff);                                  // WHITE
699
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
700
      paint.setColor(0xffb5651d);                                  // BROWN
701
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
702

    
703
      mTexture.setTexture(bitmap);
704
      }
705

    
706
///////////////////////////////////////////////////////////////////////////////////////////////////
707

    
708
    void recomputeScaleFactor(int screenWidth, int screenHeight, float size)
709
      {
710
      int texW = mNodeTexture.getWidth();
711
      int texH = mNodeTexture.getHeight();
712

    
713
      if( (float)texH/texW > (float)screenHeight/screenWidth )
714
        {
715
        int w = (screenHeight*texW)/texH;
716
        float factor = (float)screenHeight/texH;
717
        mNodeMove.set((screenWidth-w)*0.5f ,0, 0);
718
        mNodeScale.set(factor,factor,factor);
719
        }
720
      else
721
        {
722
        int h = (screenWidth*texH)/texW;
723
        float factor = (float)screenWidth/texW;
724
        mNodeMove.set(0,(screenHeight-h)*0.5f,0);
725
        mNodeScale.set(factor,factor,factor);
726
        }
727

    
728
      float scaleFactor = (size/(TEXTURE_SIZE*mSize)) * (float)texW/(screenWidth>screenHeight ? screenHeight:screenWidth);
729

    
730
      mMove.set( texW*0.5f , texH*0.5f , 0.0f );
731
      mScale.set(scaleFactor,scaleFactor,scaleFactor);
732
      }
733
}
(3-3/6)