Project

General

Profile

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

magiccube / src / main / java / org / distorted / magic / RubikCube.java @ 434f2f5a

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.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.DistortedScreen;
35
import org.distorted.library.main.DistortedTexture;
36
import org.distorted.library.mesh.MeshCubes;
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

    
43
///////////////////////////////////////////////////////////////////////////////////////////////////
44

    
45
public class RubikCube
46
{
47
    private static final int POST_ROTATION_MILLISEC = 500;
48
    private static final int TEXTURE_SIZE = 100;
49

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

    
54
    private DistortedNode[][][] mNodes;
55
    private MeshCubes[][][] mCubes;
56
    private DistortedEffects[][][] mEffects;
57
    private Static4D[][][] mQuatScramble;
58
    private Static3D[][][] mRotationAxis;
59
    private Dynamic1D[][][] mRotationAngle;
60
    private Static3D[][][] mCurrentPosition;
61
    private MatrixEffectRotate[][][] mRotateEffect;
62
    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
63
    private Static3D mMove, mScale;
64
    private DistortedTexture mTexture;
65
    private DistortedEffects mEffectsListeningForNow;
66

    
67
    private int mRotAxis, mRotRow;
68
    private int mSize;
69

    
70
///////////////////////////////////////////////////////////////////////////////////////////////////
71

    
72
    RubikCube(int size, Static4D quatC, Static4D quatA)
73
      {
74
      mSize = size;
75

    
76
      mRotationAngleStatic = new Static1D(0);
77
      mRotationAngleMiddle = new Static1D(0);
78
      mRotationAngleFinal  = new Static1D(0);
79

    
80
      mMove = new Static3D(0,0,0);
81
      mScale= new Static3D(1,1,1);
82

    
83
      mRotAxis= RubikSurfaceView.VECTX;
84
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
85

    
86
      mNodes          = new DistortedNode[mSize][mSize][mSize];
87
      mCubes          = new MeshCubes[mSize][mSize][mSize];
88
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
89
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
90
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
91
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
92
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
93
      mRotateEffect   = new MatrixEffectRotate[mSize][mSize][mSize];
94

    
95
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
96

    
97
      Static3D center = new Static3D(TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f);
98
      Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
99

    
100
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
101
      MatrixEffectMove        moveEffect = new MatrixEffectMove(mMove);
102
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(mScale);
103
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
104
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
105

    
106
      // 3x2 bitmap = 6 squares:
107
      //
108
      // RED     GREEN   BLUE
109
      // YELLOW  WHITE   BROWN
110

    
111
      final float ze = 0.0f;
112
      final float ot = 1.0f/3.0f;
113
      final float tt = 2.0f/3.0f;
114
      final float oh = 1.0f/2.0f;
115
      final float of = 1.0f/40.0f;
116

    
117
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
118
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
119
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
120
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
121
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
122
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
123

    
124
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
125

    
126
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
127
      float nc = 0.5f*(mSize-1);
128
      int vertices = (int)(24.0f/mSize + 2.0f);
129

    
130
      for(int x = 0; x< mSize; x++)
131
        for(int y = 0; y< mSize; y++)
132
          for(int z = 0; z< mSize; z++)
133
            {
134
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
135
              {
136
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
137
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
138
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
139
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
140
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
141
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
142

    
143
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
144
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
145
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
146
              mRotationAngle[x][y][z]   = new Dynamic1D();
147
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
148
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
149
              mRotateEffect[x][y][z]    = new MatrixEffectRotate(mRotationAngle[x][y][z], mRotationAxis[x][y][z], center);
150

    
151
              mEffects[x][y][z] = new DistortedEffects();
152
              mEffects[x][y][z].apply(sinkEffect);
153
              mEffects[x][y][z].apply(moveEffect);
154
              mEffects[x][y][z].apply(scaleEffect);
155
              mEffects[x][y][z].apply(quatCEffect);
156
              mEffects[x][y][z].apply(quatAEffect);
157
              mEffects[x][y][z].apply(mRotateEffect[x][y][z]);
158
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
159
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
160
              }
161
            }
162
      }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165

    
166
    public void attachToScreen(DistortedScreen screen)
167
      {
168
      for(int x=0; x<mSize; x++)
169
        for(int y=0; y<mSize; y++)
170
          for(int z=0; z<mSize; z++)
171
            {
172
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
173
              {
174
              mNodes[x][y][z] = new DistortedNode(mTexture,mEffects[x][y][z],mCubes[x][y][z]);
175
              screen.attach(mNodes[x][y][z]);
176
              }
177
            }
178
      }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
    public void apply(Effect effect)
183
      {
184
      for(int x=0; x<mSize; x++)
185
        for(int y=0; y<mSize; y++)
186
          for(int z=0; z<mSize; z++)
187
            {
188
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
189
              {
190
              mEffects[x][y][z].apply(effect);
191
              }
192
            }
193
      }
194

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196

    
197
    public void registerForMessages(EffectListener listener)
198
      {
199
      mEffects[0][0][0].registerForMessages(listener);
200
      }
201

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

    
204
    public void deregisterForMessages(EffectListener listener)
205
      {
206
      mEffects[0][0][0].deregisterForMessages(listener);
207
      }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
211
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
212
// memory leak.
213

    
214
    void releaseResources()
215
      {
216
      mTexture.markForDeletion();
217

    
218
      for(int x=0; x<mSize; x++)
219
        for(int y=0; y<mSize; y++)
220
          for(int z=0; z<mSize; z++)
221
            {
222
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
223
              {
224
              mCubes[x][y][z].markForDeletion();
225
              mNodes[x][y][z].markForDeletion();
226
              }
227
            }
228
      }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231

    
232
    void addNewRotation(int vector, float offset )
233
      {
234
      Static3D axis = VectX;
235

    
236
      switch(vector)
237
        {
238
        case RubikSurfaceView.VECTX: axis = VectX; break;
239
        case RubikSurfaceView.VECTY: axis = VectY; break;
240
        case RubikSurfaceView.VECTZ: axis = VectZ; break;
241
        }
242

    
243
      mRotAxis = vector;
244
      mRotRow  = (int)(mSize*offset);
245

    
246
      mRotationAngleStatic.set1(0.0f);
247

    
248
      for(int x=0; x<mSize; x++)
249
        for(int y=0; y<mSize; y++)
250
          for(int z=0; z<mSize; z++)
251
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
252
              {
253
              if( belongsToRotation(x,y,z,vector,mRotRow) )
254
                {
255
                mRotationAxis[x][y][z].set(axis);
256
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
257
                }
258
              }
259
      }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262

    
263
    void continueRotation(float angleInDegrees)
264
      {
265
      mRotationAngleStatic.set1(angleInDegrees);
266
      }
267

    
268
///////////////////////////////////////////////////////////////////////////////////////////////////
269

    
270
    private int computeNearestAngle(float angle)
271
      {
272
      final int NEAREST = 90;
273

    
274
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
275
      if( angle< -(NEAREST/2) ) tmp-=1;
276

    
277
      return NEAREST*tmp;
278
      }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281

    
282
    long finishRotationNow(EffectListener listener)
283
      {
284
      boolean first = true;
285
      float startingAngle = mRotationAngleStatic.get1();
286
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
287
      long effectID=0;
288

    
289
      mRotationAngleFinal.set1(nearestAngleInDegrees);
290
      mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
291

    
292
      for(int x=0; x<mSize; x++)
293
        for(int y=0; y<mSize; y++)
294
          for(int z=0; z<mSize; z++)
295
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
296
              {
297
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
298
                {
299
                mRotationAngle[x][y][z].makeRunNowFor(POST_ROTATION_MILLISEC);
300
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
301
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
302

    
303
                if( first )
304
                  {
305
                  first = false;
306
                  mEffectsListeningForNow = mEffects[x][y][z];
307
                  mEffectsListeningForNow.registerForMessages(listener);
308
                  effectID = mRotateEffect[x][y][z].getID();
309
                  }
310
                }
311
              }
312

    
313
      return effectID;
314
      }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317

    
318
    void removeRotationNow(EffectListener listener)
319
      {
320
      mEffectsListeningForNow.deregisterForMessages(listener);
321

    
322
      int nearestAngleInDegrees = computeNearestAngle(mRotationAngleStatic.get1());
323
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
324
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
325
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
326

    
327
      mRotationAngleStatic.set1(0);
328

    
329
      float qx=0,qy=0,qz=0;
330

    
331
      switch(mRotAxis)
332
        {
333
        case RubikSurfaceView.VECTX: qx=1; break;
334
        case RubikSurfaceView.VECTY: qy=1; break;
335
        case RubikSurfaceView.VECTZ: qz=1; break;
336
        }
337

    
338
      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
339

    
340
      for(int x=0; x<mSize; x++)
341
        for(int y=0; y<mSize; y++)
342
          for(int z=0; z<mSize; z++)
343
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
344
              {
345
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
346
                {
347
                mRotationAngle[x][y][z].makeRunNowFor(0);
348
                mRotationAngle[x][y][z].removeAll();
349
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
350
                modifyCurrentPosition(x,y,z,quat);
351
                }
352
              }
353
      }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356

    
357
    private float getSinkStrength()
358
      {
359
      switch(mSize)
360
        {
361
        case 1 : return 1.1f;
362
        case 2 : return 1.5f;
363
        case 3 : return 1.8f;
364
        case 4 : return 2.0f;
365
        default: return 3.0f - 4.0f/mSize;
366
        }
367
      }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370

    
371
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
372
      {
373
      switch(vector)
374
        {
375
        case RubikSurfaceView.VECTX: return mCurrentPosition[x][y][z].get1()==row;
376
        case RubikSurfaceView.VECTY: return mCurrentPosition[x][y][z].get2()==row;
377
        case RubikSurfaceView.VECTZ: return mCurrentPosition[x][y][z].get3()==row;
378
        }
379

    
380
      return false;
381
      }
382

    
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384

    
385
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
386
      {
387
      Static3D current = mCurrentPosition[x][y][z];
388
      float diff = 0.5f*(mSize-1);
389
      float cubitCenterX = current.get1() - diff;
390
      float cubitCenterY = current.get2() - diff;
391
      float cubitCenterZ = current.get3() - diff;
392

    
393
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
394
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
395

    
396
      float rotatedX = rotatedCenter.get1() + diff;
397
      float rotatedY = rotatedCenter.get2() + diff;
398
      float rotatedZ = rotatedCenter.get3() + diff;
399

    
400
      int roundedX = (int)(rotatedX+0.1f);
401
      int roundedY = (int)(rotatedY+0.1f);
402
      int roundedZ = (int)(rotatedZ+0.1f);
403

    
404
      mCurrentPosition[x][y][z].set1(roundedX);
405
      mCurrentPosition[x][y][z].set2(roundedY);
406
      mCurrentPosition[x][y][z].set3(roundedZ);
407
      }
408

    
409
///////////////////////////////////////////////////////////////////////////////////////////////////
410

    
411
    void createTexture()
412
      {
413
      Bitmap bitmap;
414

    
415
      final int S = 128;
416
      final int W = 3*S;
417
      final int H = 2*S;
418
      final int R = S/10;
419
      final int M = S/20;
420

    
421
      Paint paint = new Paint();
422
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
423
      Canvas canvas = new Canvas(bitmap);
424

    
425
      paint.setAntiAlias(true);
426
      paint.setTextAlign(Paint.Align.CENTER);
427
      paint.setStyle(Paint.Style.FILL);
428

    
429
      // 3x2 bitmap = 6 squares:
430
      //
431
      // RED     GREEN   BLUE
432
      // YELLOW  WHITE   BROWN
433

    
434
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
435
      canvas.drawRect(0, 0, W, H, paint);                          //
436

    
437
      paint.setColor(0xffff0000);                                  // RED
438
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
439
      paint.setColor(0xff00ff00);                                  // GREEN
440
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
441
      paint.setColor(0xff0000ff);                                  // BLUE
442
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
443
      paint.setColor(0xffffff00);                                  // YELLOW
444
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
445
      paint.setColor(0xffffffff);                                  // WHITE
446
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
447
      paint.setColor(0xffb5651d);                                  // BROWN
448
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
449

    
450
      mTexture.setTexture(bitmap);
451
      }
452

    
453
///////////////////////////////////////////////////////////////////////////////////////////////////
454

    
455
   void recomputeScaleFactor(int screenWidth, int screenHeight, float size)
456
     {
457
     float scaleFactor = size/(TEXTURE_SIZE*mSize);
458

    
459
     mMove.set( (screenWidth-scaleFactor*TEXTURE_SIZE)/2 , (screenHeight-scaleFactor*TEXTURE_SIZE)/2 , -scaleFactor*TEXTURE_SIZE/2 );
460
     mScale.set(scaleFactor,scaleFactor,scaleFactor);
461
     }
462

    
463
///////////////////////////////////////////////////////////////////////////////////////////////////
464

    
465
    int getSize()
466
      {
467
      return mSize;
468
      }
469
}
(2-2/4)