Project

General

Profile

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

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

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.content.SharedPreferences;
23
import android.graphics.Bitmap;
24
import android.graphics.Canvas;
25
import android.graphics.Paint;
26

    
27
import org.distorted.library.effect.Effect;
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.main.DistortedEffects;
32
import org.distorted.library.main.DistortedNode;
33
import org.distorted.library.main.DistortedTexture;
34
import org.distorted.library.mesh.MeshCubes;
35
import org.distorted.library.mesh.MeshRectangles;
36
import org.distorted.library.message.EffectListener;
37
import org.distorted.library.type.Dynamic1D;
38
import org.distorted.library.type.Static1D;
39
import org.distorted.library.type.Static3D;
40
import org.distorted.library.type.Static4D;
41
import org.distorted.magic.RubikSurfaceView;
42

    
43
import static org.distorted.object.RubikObjectList.VECTX;
44
import static org.distorted.object.RubikObjectList.VECTY;
45
import static org.distorted.object.RubikObjectList.VECTZ;
46

    
47
///////////////////////////////////////////////////////////////////////////////////////////////////
48

    
49
class RubikCube extends RubikObject
50
{
51
    private static final Static3D VectX = new Static3D(1,0,0);
52
    private static final Static3D VectY = new Static3D(0,1,0);
53
    private static final Static3D VectZ = new Static3D(0,0,1);
54

    
55
    private static final Static3D matrCenter = new Static3D(0,0,0);
56

    
57
    /////////////////////////////////////////////////////////////////////////////////
58

    
59
    private class Cubit
60
      {
61
      private final Static3D mOrigPosition;
62

    
63
      DistortedNode mNode;
64
      MeshCubes mCube;
65
      DistortedEffects mEffect;
66
      Static4D mQuatScramble;
67
      Static3D mRotationAxis;
68
      Dynamic1D mRotationAngle;
69
      Static3D mCurrentPosition;
70

    
71
      MatrixEffectRotate mRotateEffect;
72

    
73
    /////////////////////////////////////////////////////////////////////////////////
74

    
75
      Cubit(MeshCubes mesh,Static3D vector, Static3D position)
76
        {
77
        mOrigPosition = new Static3D( position.get0(), position.get1(), position.get2() );
78

    
79
        mCube = mesh;
80
        mQuatScramble    = new Static4D(0,0,0,1);
81
        mRotationAngle   = new Dynamic1D();
82
        mRotationAxis    = new Static3D(1,0,0);
83
        mCurrentPosition = position;
84
        mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, matrCenter);
85

    
86
        mEffect = new DistortedEffects();
87
        mEffect.apply(mSinkEffect);
88
        mEffect.apply( new MatrixEffectMove(vector) );
89
        mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, matrCenter));
90
        mEffect.apply(mRotateEffect);
91
        mEffect.apply(mQuatAEffect);
92
        mEffect.apply(mQuatCEffect);
93
        mEffect.apply(mScaleEffect);
94
        mEffect.apply(mMoveEffect);
95

    
96
        mNode = new DistortedNode(mTexture,mEffect,mCube);
97
        }
98

    
99
    /////////////////////////////////////////////////////////////////////////////////
100

    
101
      void savePreferences(SharedPreferences.Editor editor)
102
        {
103
        String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
104

    
105
        editor.putFloat("qx_"+number, mQuatScramble.get0());
106
        editor.putFloat("qy_"+number, mQuatScramble.get1());
107
        editor.putFloat("qz_"+number, mQuatScramble.get2());
108
        editor.putFloat("qw_"+number, mQuatScramble.get3());
109
        }
110

    
111
    /////////////////////////////////////////////////////////////////////////////////
112

    
113
      void restorePreferences(SharedPreferences preferences)
114
        {
115
        String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
116

    
117
        float qx = preferences.getFloat("qx_"+number, 0.0f);
118
        float qy = preferences.getFloat("qy_"+number, 0.0f);
119
        float qz = preferences.getFloat("qz_"+number, 0.0f);
120
        float qw = preferences.getFloat("qw_"+number, 1.0f);
121

    
122
        mQuatScramble.set(qx,qy,qz,qw);
123
        modifyCurrentPosition( mCurrentPosition, mQuatScramble);
124
        }
125

    
126
    /////////////////////////////////////////////////////////////////////////////////
127

    
128
      long finishRotationNow(EffectListener listener)
129
        {
130
        int pointNum = mRotationAngle.getNumPoints();
131

    
132
        if( pointNum>=1 )
133
          {
134
          float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
135
          int nearestAngleInDegrees = computeNearestAngle(startingAngle);
136
          mRotationAngleStatic.set0(startingAngle);
137
          mRotationAngleFinal.set0(nearestAngleInDegrees);
138
          mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
139
          return setUpCallback(listener);
140
          }
141
        else
142
          {
143
          return 0;
144
          }
145
        }
146

    
147
    /////////////////////////////////////////////////////////////////////////////////
148

    
149
      Static4D returnRotationQuat(float qx,float qy,float qz)
150
        {
151
        int pointNum = mRotationAngle.getNumPoints();
152

    
153
        if( pointNum>=1 )
154
          {
155
          float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
156
          int nearestAngleInDegrees = computeNearestAngle(startingAngle);
157
          double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
158
          float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
159
          float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
160
          return new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
161
          }
162
        else
163
          {
164
          return null;
165
          }
166
        }
167

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

    
170
      void removeRotationNow(Static4D quat)
171
        {
172
        mRotationAngle.removeAll();
173
        mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
174
        normalizeScrambleQuat( mQuatScramble );
175
        modifyCurrentPosition( mCurrentPosition,quat);
176
        }
177

    
178
    /////////////////////////////////////////////////////////////////////////////////
179

    
180
      void releaseResources()
181
        {
182
        mCube.markForDeletion();
183
        mNode.markForDeletion();
184
        }
185

    
186
    /////////////////////////////////////////////////////////////////////////////////
187

    
188
      void solve()
189
        {
190
        mQuatScramble.set(0,0,0,1);
191
        mCurrentPosition.set(mOrigPosition);
192
        }
193

    
194
    /////////////////////////////////////////////////////////////////////////////////
195

    
196
      void beginNewRotation(Static3D axis)
197
        {
198
        mRotationAxis.set(axis);
199
        mRotationAngle.add(mRotationAngleStatic);
200
        }
201

    
202
    /////////////////////////////////////////////////////////////////////////////////
203

    
204
      void addNewRotation(Static3D axis, long durationMillis, int angle)
205
        {
206
        mRotationAxis.set(axis);
207
        mRotationAngle.setDuration(durationMillis);
208
        mRotationAngle.resetToBeginning();
209
        mRotationAngle.add(new Static1D(0));
210
        mRotationAngle.add(new Static1D(angle));
211
        }
212

    
213

    
214
    /////////////////////////////////////////////////////////////////////////////////
215

    
216
      long setUpCallback(EffectListener listener)
217
        {
218
        mRotateEffect.notifyWhenFinished(listener);
219
        return mRotateEffect.getID();
220
        }
221
      }
222

    
223
    private Cubit[][][] mCubits;
224

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226
// All legal rotation quats must have all four of their components equal to either
227
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
228

    
229
    float[] getLegalQuats()
230
      {
231
      final float SQ2 = 0.5f*((float)Math.sqrt(2));
232
      return new float[] { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 };
233
      }
234

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

    
237
    private boolean belongsToRotation( Static3D currentPosition, int vector, int row)
238
      {
239
      switch(vector)
240
        {
241
        case VECTX: return currentPosition.get0()==row;
242
        case VECTY: return currentPosition.get1()==row;
243
        case VECTZ: return currentPosition.get2()==row;
244
        }
245

    
246
      return false;
247
      }
248

    
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250

    
251
    RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshRectangles mesh, DistortedEffects effects)
252
      {
253
      super(size,quatCur,quatAcc,texture,mesh,effects);
254

    
255
      mRotAxis = VECTX;
256
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
257
      mCubits  = new Cubit[mSize][mSize][mSize];
258

    
259
      // 3x2 bitmap = 6 squares:
260
      //
261
      // RED     GREEN   BLUE
262
      // YELLOW  WHITE   BROWN
263

    
264
      final float ze = 0.0f;
265
      final float ot = 1.0f/3.0f;
266
      final float tt = 2.0f/3.0f;
267
      final float oh = 1.0f/2.0f;
268
      final float of = 1.0f/40.0f;
269

    
270
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
271
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
272
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
273
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
274
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
275
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
276

    
277
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
278

    
279
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
280
      float nc = 0.5f*mSize;
281
      int vertices = (int)(24.0f/mSize + 2.0f);
282

    
283
      for(int x = 0; x< mSize; x++)
284
        for(int y = 0; y< mSize; y++)
285
          for(int z = 0; z< mSize; z++)
286
            {
287
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
288
              {
289
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
290
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
291
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
292
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
293
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
294
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
295

    
296
              mCubits[x][y][z] = new Cubit(new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom),
297
                                           new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) ),
298
                                           new Static3D(x,y,z) );
299

    
300
              attach(mCubits[x][y][z].mNode);
301
              }
302
            }
303
      }
304

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306
// PUBLIC API
307
///////////////////////////////////////////////////////////////////////////////////////////////////
308
// mSize already saved as RubikStatePlay.mButton
309

    
310
   public void savePreferences(SharedPreferences.Editor editor)
311
     {
312
     for(int x=0; x<mSize; x++)
313
        for(int y=0; y<mSize; y++)
314
          for(int z=0; z<mSize; z++)
315
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
316
              {
317
              mCubits[x][y][z].savePreferences(editor);
318
              }
319
     }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322

    
323
   public void restorePreferences(SharedPreferences preferences)
324
     {
325
     for(int x=0; x<mSize; x++)
326
        for(int y=0; y<mSize; y++)
327
          for(int z=0; z<mSize; z++)
328
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
329
              {
330
              mCubits[x][y][z].restorePreferences(preferences);
331
              }
332
     }
333

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

    
336
   public long finishRotationNow(EffectListener listener)
337
     {
338
     boolean first = true;
339
     long effectID=0;
340

    
341
     for(int x=0; x<mSize; x++)
342
       for(int y=0; y<mSize; y++)
343
         for(int z=0; z<mSize; z++)
344
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
345
             {
346
             if( belongsToRotation(mCubits[x][y][z].mCurrentPosition,mRotAxis,mRotRow) )
347
               {
348
               if( first )
349
                 {
350
                 first = false;
351
                 effectID = mCubits[x][y][z].finishRotationNow(listener);
352
                 }
353

    
354
               resetRotationAngle(mCubits[x][y][z].mRotationAngle);
355
               }
356
             }
357

    
358
     return effectID;
359
     }
360

    
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
363
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
364
// memory leak.
365

    
366
   public void releaseResources()
367
     {
368
     mTexture.markForDeletion();
369

    
370
     for(int x=0; x<mSize; x++)
371
       for(int y=0; y<mSize; y++)
372
         for(int z=0; z<mSize; z++)
373
           {
374
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
375
             {
376
             mCubits[x][y][z].releaseResources();
377
             }
378
           }
379
     }
380

    
381
///////////////////////////////////////////////////////////////////////////////////////////////////
382

    
383
   public void createTexture()
384
     {
385
     Bitmap bitmap;
386

    
387
     final int S = 128;
388
     final int W = 3*S;
389
     final int H = 2*S;
390
     final int R = S/10;
391
     final int M = S/20;
392

    
393
     Paint paint = new Paint();
394
     bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
395
     Canvas canvas = new Canvas(bitmap);
396

    
397
     paint.setAntiAlias(true);
398
     paint.setTextAlign(Paint.Align.CENTER);
399
     paint.setStyle(Paint.Style.FILL);
400

    
401
     // 3x2 bitmap = 6 squares:
402
     //
403
     // RED     GREEN   BLUE
404
     // YELLOW  WHITE   BROWN
405

    
406
     paint.setColor(0xff000000);                                  // BLACK BACKGROUND
407
     canvas.drawRect(0, 0, W, H, paint);                          //
408

    
409
     paint.setColor(0xffff0000);                                  // RED
410
     canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
411
     paint.setColor(0xff00ff00);                                  // GREEN
412
     canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
413
     paint.setColor(0xff0000ff);                                  // BLUE
414
     canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
415
     paint.setColor(0xffffff00);                                  // YELLOW
416
     canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
417
     paint.setColor(0xffffffff);                                  // WHITE
418
     canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
419
     paint.setColor(0xffb5651d);                                  // BROWN
420
     canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
421

    
422
     mTexture.setTexture(bitmap);
423
     }
424

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

    
427
   public void apply(Effect effect, int position)
428
     {
429
     for(int x=0; x<mSize; x++)
430
       for(int y=0; y<mSize; y++)
431
         for(int z=0; z<mSize; z++)
432
           {
433
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
434
             {
435
             mCubits[x][y][z].mEffect.apply(effect, position);
436
             }
437
           }
438
      }
439

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

    
442
   public void remove(long effectID)
443
     {
444
     for(int x=0; x<mSize; x++)
445
       for(int y=0; y<mSize; y++)
446
         for(int z=0; z<mSize; z++)
447
           {
448
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
449
             {
450
             mCubits[x][y][z].mEffect.abortById(effectID);
451
             }
452
           }
453
      }
454

    
455
///////////////////////////////////////////////////////////////////////////////////////////////////
456

    
457
   public void solve()
458
     {
459
     for(int x=0; x<mSize; x++)
460
       for(int y=0; y<mSize; y++)
461
         for(int z=0; z<mSize; z++)
462
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
463
             {
464
             mCubits[x][y][z].solve();
465
             }
466
      }
467

    
468
///////////////////////////////////////////////////////////////////////////////////////////////////
469

    
470
   public boolean isSolved()
471
     {
472
     Static4D q = mCubits[0][0][0].mQuatScramble;
473

    
474
     float x = q.get0();
475
     float y = q.get1();
476
     float z = q.get2();
477
     float w = q.get3();
478

    
479
     for(int i = 0; i< mSize; i++)
480
       for(int j = 0; j< mSize; j++)
481
         for(int k = 0; k< mSize; k++)
482
           {
483
           if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 )
484
             {
485
             q = mCubits[i][j][k].mQuatScramble;
486

    
487
             if( q.get0()!=x || q.get1()!=y || q.get2()!=z || q.get3()!=w )
488
               {
489
               return false;
490
               }
491
             }
492
           }
493

    
494
     return true;
495
     }
496

    
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498

    
499
   public void beginNewRotation(int vector, int row )
500
     {
501
     Static3D axis = VectX;
502

    
503
     switch(vector)
504
       {
505
       case VECTX: axis = VectX; break;
506
       case VECTY: axis = VectY; break;
507
       case VECTZ: axis = VectZ; break;
508
       }
509

    
510
     mRotAxis = vector;
511
     mRotRow  = row;
512

    
513
     mRotationAngleStatic.set0(0.0f);
514

    
515
     for(int x=0; x<mSize; x++)
516
       for(int y=0; y<mSize; y++)
517
         for(int z=0; z<mSize; z++)
518
           if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
519
             {
520
             if( belongsToRotation( mCubits[x][y][z].mCurrentPosition,vector,mRotRow) )
521
               {
522
               mCubits[x][y][z].beginNewRotation(axis);
523
               }
524
             }
525
     }
526

    
527
///////////////////////////////////////////////////////////////////////////////////////////////////
528

    
529
   public long addNewRotation(int vector, int row, int angle, long durationMillis, EffectListener listener )
530
      {
531
      Static3D axis = VectX;
532
      long effectID=0;
533
      boolean first = true;
534

    
535
      switch(vector)
536
        {
537
        case VECTX: axis = VectX; break;
538
        case VECTY: axis = VectY; break;
539
        case VECTZ: axis = VectZ; break;
540
        }
541

    
542
      mRotAxis = vector;
543
      mRotRow  = row;
544

    
545
      mRotationAngleStatic.set0(0.0f);
546

    
547
      for(int x=0; x<mSize; x++)
548
        for(int y=0; y<mSize; y++)
549
          for(int z=0; z<mSize; z++)
550
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
551
              {
552
              if( belongsToRotation(mCubits[x][y][z].mCurrentPosition,vector,mRotRow) )
553
                {
554
                mCubits[x][y][z].addNewRotation(axis,durationMillis,angle);
555

    
556
                if( first )
557
                  {
558
                  first = false;
559
                  effectID = mCubits[x][y][z].setUpCallback(listener);
560
                  }
561
                }
562
              }
563

    
564
      return effectID;
565
      }
566

    
567
///////////////////////////////////////////////////////////////////////////////////////////////////
568

    
569
   public void removeRotationNow()
570
      {
571
      float qx=0,qy=0,qz=0;
572
      boolean first = true;
573
      Static4D quat = null;
574

    
575
      switch(mRotAxis)
576
        {
577
        case VECTX: qx=1; break;
578
        case VECTY: qy=1; break;
579
        case VECTZ: qz=1; break;
580
        }
581

    
582
      for(int x=0; x<mSize; x++)
583
        for(int y=0; y<mSize; y++)
584
          for(int z=0; z<mSize; z++)
585
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
586
              {
587
              if( belongsToRotation(mCubits[x][y][z].mCurrentPosition,mRotAxis,mRotRow) )
588
                {
589
                if( first )
590
                  {
591
                  first = false;
592
                  quat = mCubits[x][y][z].returnRotationQuat(qx,qy,qz);
593
                  }
594

    
595
                mCubits[x][y][z].removeRotationNow(quat);
596
                }
597
              }
598

    
599
      mRotationAngleStatic.set0(0);
600
      }
601
}
(1-1/5)