Project

General

Profile

« Previous | Next » 

Revision 6e18bd32

Added by Leszek Koltunski over 5 years ago

Turn the Rubik app into a MemoryTest.

View differences:

src/main/java/org/distorted/examples/projection/ProjectionActivity.java
43 43
      super.onCreate(savedState);
44 44
      setContentView(R.layout.projectionlayout);
45 45

  
46
      textF = (TextView)findViewById(R.id.projectionTextFOV);
47
      textN = (TextView)findViewById(R.id.projectionTextNear);
46
      textF = findViewById(R.id.projectionTextFOV);
47
      textN = findViewById(R.id.projectionTextNear);
48 48

  
49
      SeekBar bar1 = (SeekBar)findViewById(R.id.projectionSeekFOV);
50
      SeekBar bar2 = (SeekBar)findViewById(R.id.projectionSeekNear);
49
      SeekBar bar1 = findViewById(R.id.projectionSeekFOV);
50
      SeekBar bar2 = findViewById(R.id.projectionSeekNear);
51 51

  
52 52
      bar1.setOnSeekBarChangeListener(this);
53 53
      bar2.setOnSeekBarChangeListener(this);
......
69 69
    @Override
70 70
    protected void onPause() 
71 71
      {
72
      GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.surfaceViewProjection);
72
      GLSurfaceView mView = findViewById(R.id.surfaceViewProjection);
73 73
      mView.onPause();
74 74

  
75 75
      Distorted.onPause();
......
83 83
      {
84 84
      super.onResume();
85 85
      
86
      GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.surfaceViewProjection);
86
      GLSurfaceView mView = findViewById(R.id.surfaceViewProjection);
87 87
      mView.onResume();    
88 88
      }
89 89
 
......
101 101
    public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) 
102 102
      {
103 103
      float ret;
104
      ProjectionSurfaceView view = (ProjectionSurfaceView)findViewById(R.id.surfaceViewProjection);
104
      ProjectionSurfaceView view = findViewById(R.id.surfaceViewProjection);
105 105
      ProjectionRenderer renderer = view.getRenderer();
106 106

  
107 107
      switch (bar.getId()) 
src/main/java/org/distorted/examples/rubik/RubikActivity.java
20 20
package org.distorted.examples.rubik;
21 21

  
22 22
import android.app.Activity;
23
import android.graphics.PorterDuff;
24
import android.graphics.drawable.Drawable;
23
import android.app.ActivityManager;
25 24
import android.opengl.GLSurfaceView;
26 25
import android.os.Bundle;
27
import android.support.v4.content.ContextCompat;
28
import android.view.View;
26
import android.widget.TextView;
29 27

  
30 28
import org.distorted.examples.R;
31 29
import org.distorted.library.main.Distorted;
......
34 32

  
35 33
public class RubikActivity extends Activity
36 34
{
37
            static final int DEFAULT_SIZE  = 3;
38
    private static final int SMALLEST_SIZE = 2;
39
    private static final int[] button_ids  = {R.id.rubikSize2, R.id.rubikSize3, R.id.rubikSize4};
35
    private TextView mText;
40 36

  
41 37
///////////////////////////////////////////////////////////////////////////////////////////////////
42 38

  
......
46 42
      super.onCreate(icicle);
47 43
      setContentView(R.layout.rubiklayout);
48 44

  
49
      markButton(DEFAULT_SIZE);
45
      mText = findViewById(R.id.rubikText);
46
      setText(1);
50 47
      }
51 48

  
52 49
///////////////////////////////////////////////////////////////////////////////////////////////////
......
81 78

  
82 79
///////////////////////////////////////////////////////////////////////////////////////////////////
83 80

  
84
    public void Scramble(View v)
81
    void setText(int numCube)
85 82
      {
86
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
87
      view.scrambleCube();
83
      long availMemory = returnAvailableMemory();
84
      int  availMemoryInMB = (int)(availMemory/1000000);
85
      mText.setText(getString(R.string.rubik_placeholder, numCube, availMemoryInMB));
88 86
      }
89 87

  
90 88
///////////////////////////////////////////////////////////////////////////////////////////////////
91 89

  
92
    public void Credits(View v)
93
      {
94
      android.util.Log.e("rubik", "credits...");
95
      }
96

  
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98

  
99
    public void setSize(View v)
100
      {
101
      int size=0, id = v.getId();
102

  
103
      for(int b=0; b<button_ids.length; b++)
104
        if( button_ids[b] == id )
105
          {
106
          size = b+SMALLEST_SIZE;
107
          break;
108
          }
109

  
110
      markButton(size);
111

  
112
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
113
      view.setNewCubeSize(size);
114
      }
115

  
116
///////////////////////////////////////////////////////////////////////////////////////////////////
117

  
118
   private void markButton(int size)
90
   private long returnAvailableMemory()
119 91
     {
120
     for(int b=0; b<button_ids.length; b++)
121
       {
122
       Drawable d = findViewById(button_ids[b]).getBackground();
92
     ActivityManager activityManager = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
93
     ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
94
     activityManager.getMemoryInfo(memoryInfo);
123 95

  
124
       if( size == b+SMALLEST_SIZE )
125
         {
126
         d.setColorFilter(ContextCompat.getColor(this,R.color.red), PorterDuff.Mode.MULTIPLY);
127
         }
128
       else
129
         {
130
         d.clearColorFilter();
131
         }
132
       }
96
     return memoryInfo.availMem;
133 97
     }
134 98
}
src/main/java/org/distorted/examples/rubik/RubikCube.java
19 19

  
20 20
package org.distorted.examples.rubik;
21 21

  
22
import java.util.Random;
23

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

  
44 46
class RubikCube
45 47
{
46
    private static final int POST_ROTATION_MILLISEC = 500;
48
    private static final int VECTX = 0;  //
49
    private static final int VECTY = 1;  // don't change this
50
    private static final int VECTZ = 2;  //
51

  
52
    private static final int ROTATION_MILLISEC = 1500;
47 53
    private static final int TEXTURE_SIZE = 100;
48 54

  
49 55
    private static final Static3D VectX = new Static3D(1,0,0);
......
53 59
    private DistortedNode[][][] mNodes;
54 60
    private MeshCubes[][][] mCubes;
55 61
    private DistortedEffects[][][] mEffects;
56
    private Static4D[][][] mQuatScramble;
57 62
    private Static3D[][][] mRotationAxis;
58 63
    private Dynamic1D[][][] mRotationAngle;
59
    private Static3D[][][] mCurrentPosition;
60
    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
61 64
    private DistortedTexture mTexture;
62
    private DistortedEffects mEffectsListeningForNow;
63 65

  
64
    private int mRotAxis, mRotRow;
65 66
    private int mSize;
66 67

  
68
    private Random mRnd = new Random(0);
69

  
67 70
///////////////////////////////////////////////////////////////////////////////////////////////////
68 71

  
69
    RubikCube(int size, Static3D move, Static3D scale, Static4D quatC, Static4D quatA)
72
    RubikCube(int size, Static3D move, Static3D scale)
70 73
      {
71 74
      mSize = size;
72 75

  
73
      mRotationAngleStatic = new Static1D(0);
74
      mRotationAngleMiddle = new Static1D(0);
75
      mRotationAngleFinal  = new Static1D(0);
76

  
77
      mRotAxis= RubikSurfaceView.VECTX;
78 76
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
79 77

  
80 78
      mNodes          = new DistortedNode[mSize][mSize][mSize];
81 79
      mCubes          = new MeshCubes[mSize][mSize][mSize];
82 80
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
83
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
84 81
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
85 82
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
86
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
87 83

  
88 84
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
89 85

  
......
93 89
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
94 90
      MatrixEffectMove        moveEffect = new MatrixEffectMove(move);
95 91
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(scale);
96
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
97
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
92
      MatrixEffectQuaternion  quatEffect = new MatrixEffectQuaternion( initializeQuat(), center);
98 93

  
99 94
      // 3x2 bitmap = 6 squares:
100 95
      //
......
135 130

  
136 131
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
137 132
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
138
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
139 133
              mRotationAngle[x][y][z]   = new Dynamic1D();
140 134
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
141
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
135

  
136
              mRotationAngle[x][y][z].add(new Static1D(0.0f));
137
              mRotationAngle[x][y][z].add(new Static1D(0.0f));
142 138

  
143 139
              mEffects[x][y][z] = new DistortedEffects();
144 140
              mEffects[x][y][z].apply(sinkEffect);
145 141
              mEffects[x][y][z].apply(moveEffect);
146 142
              mEffects[x][y][z].apply(scaleEffect);
147
              mEffects[x][y][z].apply(quatCEffect);
148
              mEffects[x][y][z].apply(quatAEffect);
143
              mEffects[x][y][z].apply(quatEffect);
149 144
              mEffects[x][y][z].apply( new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], center));
150
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
151 145
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
152 146
              }
153 147
            }
......
192 186

  
193 187
///////////////////////////////////////////////////////////////////////////////////////////////////
194 188

  
195
    void addNewRotation(int vector, float offset )
189
    void addRotation(EffectListener listener)
196 190
      {
191
      mRnd.setSeed(System.currentTimeMillis());
192

  
193
      int vector = mRnd.nextInt(3);
194
      int rotRow = mRnd.nextInt(mSize);
195

  
196
      boolean first = true;
197 197
      Static3D axis = VectX;
198 198

  
199 199
      switch(vector)
200 200
        {
201
        case RubikSurfaceView.VECTX: axis = VectX; break;
202
        case RubikSurfaceView.VECTY: axis = VectY; break;
203
        case RubikSurfaceView.VECTZ: axis = VectZ; break;
201
        case VECTX: axis = VectX; break;
202
        case VECTY: axis = VectY; break;
203
        case VECTZ: axis = VectZ; break;
204 204
        }
205 205

  
206
      mRotAxis = vector;
207
      mRotRow  = (int)(mSize*offset);
208

  
209
      mRotationAngleStatic.set1(0.0f);
210

  
211 206
      for(int x=0; x<mSize; x++)
212 207
        for(int y=0; y<mSize; y++)
213 208
          for(int z=0; z<mSize; z++)
214 209
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
215 210
              {
216
              if( belongsToRotation(x,y,z,vector,mRotRow) )
211
              if( belongsToRotation(x,y,z,vector,rotRow) )
217 212
                {
218 213
                mRotationAxis[x][y][z].set(axis);
219
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
220
                }
221
              }
222
      }
223

  
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

  
226
    void continueRotation(float angleInDegrees)
227
      {
228
      mRotationAngleStatic.set1(angleInDegrees);
229
      }
230

  
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

  
233
    private int computeNearestAngle(float angle)
234
      {
235
      final int NEAREST = 90;
236

  
237
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
238
      if( angle< -(NEAREST/2) ) tmp-=1;
239

  
240
      return NEAREST*tmp;
241
      }
242

  
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

  
245
    void finishRotationNow(EffectListener listener)
246
      {
247
      boolean first = true;
248
      float startingAngle = mRotationAngleStatic.get1();
249
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
250

  
251
      mRotationAngleFinal.set1(nearestAngleInDegrees);
252
      mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
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
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
260
                {
261
                mRotationAngle[x][y][z].makeRunNowFor(POST_ROTATION_MILLISEC);
262
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
263
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
214
                mRotationAngle[x][y][z].makeRunNowFor(ROTATION_MILLISEC);
215
                mRotationAngle[x][y][z].setPoint(1,90.0f);
264 216

  
265 217
                if( first )
266 218
                  {
267 219
                  first = false;
268
                  mEffectsListeningForNow = mEffects[x][y][z];
269
                  mEffectsListeningForNow.registerForMessages(listener);
220
                  mEffects[x][y][z].registerForMessages(listener);
270 221
                  }
271 222
                }
272 223
              }
273 224
      }
274 225

  
275 226
///////////////////////////////////////////////////////////////////////////////////////////////////
227
// Initial rotation of the cube. Something semi-random that looks good.
276 228

  
277
    void removeRotationNow(EffectListener listener)
278
      {
279
      mEffectsListeningForNow.deregisterForMessages(listener);
280

  
281
      int nearestAngleInDegrees = computeNearestAngle(mRotationAngleStatic.get1());
282
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
283
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
284
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
285

  
286
      mRotationAngleStatic.set1(0);
229
   private Static4D initializeQuat()
230
     {
231
     return new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
232
     }
287 233

  
288
      float qx=0,qy=0,qz=0;
234
///////////////////////////////////////////////////////////////////////////////////////////////////
289 235

  
290
      switch(mRotAxis)
236
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
237
      {
238
      switch(vector)
291 239
        {
292
        case RubikSurfaceView.VECTX: qx=1; break;
293
        case RubikSurfaceView.VECTY: qy=1; break;
294
        case RubikSurfaceView.VECTZ: qz=1; break;
240
        case VECTX: return x==row;
241
        case VECTY: return y==row;
242
        case VECTZ: return z==row;
295 243
        }
296 244

  
297
      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
298

  
299
      for(int x=0; x<mSize; x++)
300
        for(int y=0; y<mSize; y++)
301
          for(int z=0; z<mSize; z++)
302
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
303
              {
304
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
305
                {
306
                mRotationAngle[x][y][z].makeRunNowFor(0);
307
                mRotationAngle[x][y][z].removeAll();
308
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
309
                modifyCurrentPosition(x,y,z,quat);
310
                }
311
              }
245
      return false;
312 246
      }
313 247

  
314 248
///////////////////////////////////////////////////////////////////////////////////////////////////
......
325 259
        }
326 260
      }
327 261

  
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329

  
330
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
331
      {
332
      switch(vector)
333
        {
334
        case RubikSurfaceView.VECTX: return mCurrentPosition[x][y][z].get1()==row;
335
        case RubikSurfaceView.VECTY: return mCurrentPosition[x][y][z].get2()==row;
336
        case RubikSurfaceView.VECTZ: return mCurrentPosition[x][y][z].get3()==row;
337
        }
338

  
339
      return false;
340
      }
341

  
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

  
344
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
345
      {
346
      Static3D current = mCurrentPosition[x][y][z];
347
      float diff = 0.5f*(mSize-1);
348
      float cubitCenterX = current.get1() - diff;
349
      float cubitCenterY = current.get2() - diff;
350
      float cubitCenterZ = current.get3() - diff;
351

  
352
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
353
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
354

  
355
      float rotatedX = rotatedCenter.get1() + diff;
356
      float rotatedY = rotatedCenter.get2() + diff;
357
      float rotatedZ = rotatedCenter.get3() + diff;
358

  
359
      int roundedX = (int)(rotatedX+0.1f);
360
      int roundedY = (int)(rotatedY+0.1f);
361
      int roundedZ = (int)(rotatedZ+0.1f);
362

  
363
      mCurrentPosition[x][y][z].set1(roundedX);
364
      mCurrentPosition[x][y][z].set2(roundedY);
365
      mCurrentPosition[x][y][z].set3(roundedZ);
366
      }
367

  
368 262
///////////////////////////////////////////////////////////////////////////////////////////////////
369 263

  
370 264
    void createTexture()
src/main/java/org/distorted/examples/rubik/RubikRenderer.java
38 38
{
39 39
    private static final float CUBE_SCREEN_RATIO = 0.5f;
40 40
    private static final float CAMERA_DISTANCE   = 0.6f;  // 0.6 of the length of max(scrHeight,scrWidth)
41
    private static final int MIN_CUBE_SIZE = 1;
42
    private static final int MAX_CUBE_SIZE = 6;
41 43

  
42 44
    private RubikSurfaceView mView;
43 45
    private DistortedScreen mScreen;
44 46
    private Static3D mMove, mScale;
45
    private Static4D mQuatCurrent, mQuatAccumulated;
46
    private Static4D mTempCurrent, mTempAccumulated;
47
    private float mCubeSizeInScreenSpace;
48 47
    private int mNextCubeSize;
49
    private boolean mFinishRotation, mRemoveRotation, mFinishDragCurrent, mFinishDragAccumulated;
50
    private boolean mCanRotate;
48
    private boolean mChangeCubeSizeNow;
49
    private int mNumCube;
51 50
    private RubikCube mCube;
52 51

  
53 52
    private int mScreenWidth, mScreenHeight;
......
60 59

  
61 60
      mScreen = new DistortedScreen();
62 61

  
63
      mTempCurrent     = new Static4D(0,0,0,1);
64
      mTempAccumulated = initializeQuat();
65
      mQuatCurrent     = new Static4D(0,0,0,1);
66
      mQuatAccumulated = initializeQuat();
67

  
68 62
      mScreenWidth = mScreenHeight = 0;
69 63

  
70 64
      mMove  = new Static3D(0,0,0);
71 65
      mScale = new Static3D(1,1,1);
72 66

  
73
      mFinishRotation        = false;
74
      mRemoveRotation        = false;
75
      mFinishDragCurrent     = false;
76
      mFinishDragAccumulated = false;
77

  
78
      mNextCubeSize= 0;
67
      mNextCubeSize= MIN_CUBE_SIZE;
68
      mChangeCubeSizeNow = false;
79 69

  
80
      mCanRotate = true;
70
      mNumCube = 0;
81 71
      }
82 72

  
83 73
///////////////////////////////////////////////////////////////////////////////////////////////////
84
// various things are done here delayed, 'after the next render' as not to be done mid-render and
85
// cause artifacts.
74
// change the cube size here, 'after the next render' as not to cause artifacts.
86 75

  
87 76
    public void onDrawFrame(GL10 glUnused) 
88 77
      {
89 78
      mScreen.render( System.currentTimeMillis() );
90 79

  
91
      if( mFinishDragCurrent )
92
        {
93
        mFinishDragCurrent = false;
94
        mQuatCurrent.set(mTempCurrent);
95
        }
96

  
97
      if( mFinishDragAccumulated )
98
        {
99
        mFinishDragAccumulated = false;
100
        mQuatAccumulated.set(mTempAccumulated);
101
        }
102

  
103
      if( mFinishRotation )
80
      if( mChangeCubeSizeNow )
104 81
        {
105
        mCanRotate = false;
106
        mFinishRotation=false;
107
        mCube.finishRotationNow(this);
108
        }
109

  
110
      if( mRemoveRotation )
111
        {
112
        mRemoveRotation=false;
113
        mCube.removeRotationNow(this);
114
        mCanRotate = true;
115
        }
116

  
117
      if( mNextCubeSize!=0 )
118
        {
119
        createCubeNow(mNextCubeSize);
120
        mScreen.detachAll();
121
        mCube.attachToScreen(mScreen);
122
        mNextCubeSize = 0;
123
        mCanRotate = true;   // it can happen that we have just changed the size of the cube while
124
                             // finishing rotation and never removed it!
82
        mChangeCubeSizeNow = false;
83
        createNextCube();
125 84
        }
126 85
      }
127 86

  
......
132 91
     {
133 92
     switch(em)
134 93
        {
135
        case EFFECT_FINISHED: mRemoveRotation = true; break;
94
        case EFFECT_FINISHED: mNextCubeSize++;
95
                              if( mNextCubeSize> MAX_CUBE_SIZE ) mNextCubeSize = MIN_CUBE_SIZE;
96
                              mChangeCubeSizeNow = true;
97
                              break;
136 98
        }
137 99
     }
138 100

  
......
144 106
      float fovInDegrees   = computeFOV(cameraDistance,height);
145 107

  
146 108
      mScreen.setProjection( fovInDegrees, 0.1f);
147
      mView.setScreenSize(width,height);
148
      mView.setCameraDist(cameraDistance);
149 109
      mScreen.resize(width, height);
150 110

  
151 111
      recomputeScaleFactor(width,height);
......
158 118
    
159 119
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
160 120
      {
161
      mCube.createTexture();
162
      mScreen.detachAll();
163
      mCube.attachToScreen(mScreen);
121
      createNextCube();
164 122

  
165 123
      VertexEffectSink.enable();
166 124

  
......
176 134

  
177 135
///////////////////////////////////////////////////////////////////////////////////////////////////
178 136

  
179
   private float computeFOV(float cameraDistance, int screenHeight)
180
     {
181
     double halfFOVInRadians = Math.atan( screenHeight/(2*cameraDistance) );
182
     return (float)(2*halfFOVInRadians*(180/Math.PI));
183
     }
184

  
185
///////////////////////////////////////////////////////////////////////////////////////////////////
186
// no this will not race with onDrawFrame
187

  
188
   void finishRotation()
137
   private void createNextCube()
189 138
     {
190
     mFinishRotation = true;
191
     }
139
     if( mCube!=null ) mCube.releaseResources();
140
     mCube = new RubikCube(mNextCubeSize, mMove, mScale);
141
     mCube.createTexture();
192 142

  
193
///////////////////////////////////////////////////////////////////////////////////////////////////
143
     if( mScreenWidth!=0 ) recomputeScaleFactor(mScreenWidth,mScreenHeight);
194 144

  
195
   void createCube(int newSize)
196
     {
197
     mNextCubeSize = newSize;
145
     mScreen.detachAll();
146
     mCube.attachToScreen(mScreen);
147
     mCube.addRotation(this);
148
     RubikActivity act = mView.getRubikActivity();
149
     act.setText(++mNumCube);
198 150
     }
199 151

  
200 152
///////////////////////////////////////////////////////////////////////////////////////////////////
201 153

  
202
   void createCubeNow(int newSize)
154
   private float computeFOV(float cameraDistance, int screenHeight)
203 155
     {
204
     int oldSize = mCube==null ? 0 : mCube.getSize();
205

  
206
     if( oldSize!=newSize )
207
       {
208
       if( mCube!=null ) mCube.releaseResources();
209
       mCube = new RubikCube(newSize, mMove, mScale, mQuatCurrent, mQuatAccumulated);
210
       mCube.createTexture();
211

  
212
       if( mScreenWidth!=0 )
213
         {
214
         recomputeScaleFactor(mScreenWidth,mScreenHeight);
215
         }
216
       }
156
     double halfFOVInRadians = Math.atan( screenHeight/(2*cameraDistance) );
157
     return (float)(2*halfFOVInRadians*(180/Math.PI));
217 158
     }
218 159

  
219 160
///////////////////////////////////////////////////////////////////////////////////////////////////
220 161

  
221 162
   private void recomputeScaleFactor(int screenWidth, int screenHeight)
222 163
     {
223
     mCubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
164
     float cubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
224 165
     float texSize = mCube.getTextureSize();
225
     float scaleFactor = mCubeSizeInScreenSpace/(texSize*mCube.getSize());
166
     float scaleFactor = cubeSizeInScreenSpace/(texSize*mCube.getSize());
226 167

  
227 168
     mMove.set( (screenWidth-scaleFactor*texSize)/2 , (screenHeight-scaleFactor*texSize)/2 , -scaleFactor*texSize/2 );
228 169
     mScale.set(scaleFactor,scaleFactor,scaleFactor);
229 170
     }
230

  
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

  
233
   void scrambleCube()
234
     {
235

  
236
     }
237

  
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239

  
240
   float returnCubeSizeInScreenSpace()
241
     {
242
     return mCubeSizeInScreenSpace;
243
     }
244

  
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246

  
247
   boolean canRotate()
248
     {
249
     return mCanRotate;
250
     }
251

  
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

  
254
   RubikCube getCube()
255
     {
256
     return mCube;
257
     }
258

  
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260
// Initial rotation of the cube. Something semi-random that looks good.
261

  
262
   Static4D initializeQuat()
263
     {
264
     return new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
265
     }
266

  
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

  
269
   void setQuatCurrent(Static4D current)
270
     {
271
     mTempCurrent.set(current);
272
     mFinishDragCurrent = true;
273
     }
274

  
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276

  
277
   void setQuatAccumulated(Static4D accumulated)
278
     {
279
     mTempAccumulated.set(accumulated);
280
     mFinishDragAccumulated = true;
281
     }
282 171
}
src/main/java/org/distorted/examples/rubik/RubikSurfaceView.java
24 24
import android.content.pm.ConfigurationInfo;
25 25
import android.opengl.GLSurfaceView;
26 26
import android.util.AttributeSet;
27
import android.view.MotionEvent;
28 27

  
29
import org.distorted.library.type.Static4D;
28
import java.lang.ref.WeakReference;
30 29

  
31 30
///////////////////////////////////////////////////////////////////////////////////////////////////
32 31

  
33 32
class RubikSurfaceView extends GLSurfaceView
34 33
{
35
    // Moving the finger from the middle of the vertical screen to the right edge will rotate a
36
    // given face by SWIPING_SENSITIVITY/2 degrees.
37
    private final static int SWIPING_SENSITIVITY = 240;
38

  
39
    private final static int NONE   =-1;
40
    private final static int FRONT  = 0;  // has to be 6 consecutive ints
41
    private final static int BACK   = 1;  // FRONT ... BOTTOM
42
    private final static int LEFT   = 2;  //
43
    private final static int RIGHT  = 3;  //
44
    private final static int TOP    = 4;  //
45
    private final static int BOTTOM = 5;  //
46

  
47
    static final int VECTX = 0;  //
48
    static final int VECTY = 1;  // don't change this
49
    static final int VECTZ = 2;  //
50

  
51
    private static final int[] VECT = {VECTX,VECTY,VECTZ};
52

  
53
    private boolean mDragging, mBeginningRotation, mContinuingRotation;
54
    private int mX, mY;
55
    private Static4D mQuatCurrent, mQuatAccumulated;
56
    private int mRotationVect;
57
    private RubikRenderer mRenderer;
58

  
59
    private float[] mPoint, mCamera, mTouchPointCastOntoFace, mDiff, mTouchPoint; // all in screen space
60
    private int mLastTouchedFace;
61
    private int mScreenWidth, mScreenHeight, mScreenMin;
62
    private float mCameraDistance;
34
    private WeakReference<RubikActivity> mWeakAct;
63 35

  
64 36
///////////////////////////////////////////////////////////////////////////////////////////////////
65 37

  
......
69 41

  
70 42
      if(!isInEditMode())
71 43
        {
72
        mRotationVect = VECT[0];
73

  
74
        mPoint = new float[3];
75
        mCamera= new float[3];
76
        mDiff  = new float[3];
77
        mTouchPoint = new float[3];
78
        mTouchPointCastOntoFace = new float[3];
79

  
80
        mScreenWidth = mScreenHeight = mScreenMin = 0;
81

  
82
        mRenderer = new RubikRenderer(this);
83
        mRenderer.createCubeNow(RubikActivity.DEFAULT_SIZE);
84

  
85
        mQuatCurrent     = new Static4D(0,0,0,1);
86
        mQuatAccumulated = mRenderer.initializeQuat();
44
        mWeakAct = new WeakReference<>( (RubikActivity)context);
87 45

  
88 46
        final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
89 47
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
90 48
        setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
91
        setRenderer(mRenderer);
92
        }
93
      }
94

  
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96

  
97
    @Override
98
    public boolean onTouchEvent(MotionEvent event)
99
      {
100
      int action = event.getAction();
101
      int x = (int)event.getX();
102
      int y = (int)event.getY();
103

  
104
      switch(action)
105
         {
106
         case MotionEvent.ACTION_DOWN: mX = x;
107
                                       mY = y;
108
                                       mLastTouchedFace = faceTouched(x,y);
109

  
110
                                       if( mLastTouchedFace != NONE )
111
                                         {
112
                                         mDragging           = false;
113
                                         mBeginningRotation  = mRenderer.canRotate();
114
                                         mContinuingRotation = false;
115
                                         }
116
                                       else
117
                                         {
118
                                         mDragging           = true;
119
                                         mBeginningRotation  = false;
120
                                         mContinuingRotation = false;
121
                                         }
122
                                       break;
123
         case MotionEvent.ACTION_MOVE: if( mDragging )
124
                                         {
125
                                         mQuatCurrent.set(quatFromDrag(mX-x,mY-y));
126
                                         mRenderer.setQuatCurrent(mQuatCurrent);
127
                                         }
128
                                       if( mBeginningRotation )
129
                                         {
130
                                         int minimumDistToStartRotating = (mScreenMin*mScreenMin)/100;
131

  
132
                                         if( (mX-x)*(mX-x)+(mY-y)*(mY-y) > minimumDistToStartRotating )
133
                                           {
134
                                           addNewRotation(x,y);
135
                                           mBeginningRotation = false;
136
                                           mContinuingRotation= true;
137
                                           }
138
                                         }
139
                                       else if( mContinuingRotation )
140
                                         {
141
                                         continueRotation(x,y);
142
                                         }
143
                                       break;
144
         case MotionEvent.ACTION_UP  : if( mDragging )
145
                                         {
146
                                         mQuatAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
147
                                         mQuatCurrent.set(0f, 0f, 0f, 1f);
148
                                         mRenderer.setQuatCurrent(mQuatCurrent);
149
                                         mRenderer.setQuatAccumulated(mQuatAccumulated);
150
                                         }
151

  
152
                                       if( mContinuingRotation )
153
                                         {
154
                                         finishRotation();
155
                                         }
156

  
157
                                       break;
158
         }
159

  
160
      return true;
161
      }
162

  
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164

  
165
    void setNewCubeSize(int newCubeSize)
166
      {
167
      mRenderer.createCube(newCubeSize);
168
      }
169

  
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171

  
172
    void scrambleCube()
173
      {
174
      mRenderer.scrambleCube();
175
      }
176

  
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

  
179
    void setScreenSize(int width, int height)
180
      {
181
      mScreenWidth = width;
182
      mScreenHeight= height;
183

  
184
      mScreenMin = width<height ? width:height;
185
      }
186

  
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

  
189
    void setCameraDist(float distance)
190
      {
191
      mCameraDistance = distance;
192
      }
193

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

  
196
    private int faceTouched(int xTouch, int yTouch)
197
      {
198
      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
199

  
200
      convertTouchPointToScreenSpace(xTouch,yTouch);
201
      convertCameraPointToScreenSpace();
202

  
203
      for(int face=FRONT; face<=BOTTOM; face++)
204
        {
205
        if( faceIsVisible(face,cubeHalfSize) )
206
          {
207
          castTouchPointOntoFace(face,cubeHalfSize, mTouchPointCastOntoFace);
208

  
209
          float qX= (mTouchPointCastOntoFace[0]+cubeHalfSize) / (2*cubeHalfSize);
210
          float qY= (mTouchPointCastOntoFace[1]+cubeHalfSize) / (2*cubeHalfSize);
211
          float qZ= (mTouchPointCastOntoFace[2]+cubeHalfSize) / (2*cubeHalfSize);
212

  
213
          if( qX<=1 && qX>=0 && qY<=1 && qY>=0 && qZ<=1 && qZ>=0 ) return face;
214
          }
215
        }
216

  
217
      return NONE;
218
      }
219

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

  
222
    private void addNewRotation(int x, int y)
223
      {
224
      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
225

  
226
      convertTouchPointToScreenSpace(x,y);
227
      castTouchPointOntoFace(mLastTouchedFace,cubeHalfSize,mDiff);
228

  
229
      mDiff[0] -= mTouchPointCastOntoFace[0];
230
      mDiff[1] -= mTouchPointCastOntoFace[1];
231
      mDiff[2] -= mTouchPointCastOntoFace[2];
232

  
233
      int xAxis = retFaceXaxis(mLastTouchedFace);
234
      int yAxis = retFaceYaxis(mLastTouchedFace);
235
      mRotationVect = (isVertical( mDiff[xAxis], mDiff[yAxis]) ? VECT[xAxis]:VECT[yAxis]);
236
      float offset= (mTouchPointCastOntoFace[mRotationVect]+cubeHalfSize)/(2*cubeHalfSize);
237

  
238
      mTouchPoint[0] = mPoint[0];
239
      mTouchPoint[1] = mPoint[1];
240
      mTouchPoint[2] = mPoint[2];
241

  
242
      mRenderer.getCube().addNewRotation(mRotationVect,offset);
243
      }
244

  
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246

  
247
    private boolean isVertical(float x, float y)
248
      {
249
      return (y>x) ? (y>=-x) : (y< -x);
250
      }
251

  
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

  
254
    private void continueRotation(int x, int y)
255
      {
256
      convertTouchPointToScreenSpace(x,y);
257

  
258
      mDiff[0] = mPoint[0]-mTouchPoint[0];
259
      mDiff[1] = mPoint[1]-mTouchPoint[1];
260
      mDiff[2] = mPoint[2]-mTouchPoint[2];
261

  
262
      int xAxis= retFaceXaxis(mLastTouchedFace);
263
      int yAxis= retFaceYaxis(mLastTouchedFace);
264
      int sign = retFaceRotationSign(mLastTouchedFace);
265
      float angle = (mRotationVect==xAxis ? mDiff[yAxis] : -mDiff[xAxis]);
266

  
267
      mRenderer.getCube().continueRotation(SWIPING_SENSITIVITY*sign*angle/mScreenMin);
268
      }
269

  
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

  
272
    private void finishRotation()
273
      {
274
      mRenderer.finishRotation();
275
      }
276

  
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
// return quat1*quat2
279

  
280
    static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
281
      {
282
      float qx = quat1.get1();
283
      float qy = quat1.get2();
284
      float qz = quat1.get3();
285
      float qw = quat1.get4();
286

  
287
      float rx = quat2.get1();
288
      float ry = quat2.get2();
289
      float rz = quat2.get3();
290
      float rw = quat2.get4();
291

  
292
      float tx = rw*qx - rz*qy + ry*qz + rx*qw;
293
      float ty = rw*qy + rz*qx + ry*qw - rx*qz;
294
      float tz = rw*qz + rz*qw - ry*qx + rx*qy;
295
      float tw = rw*qw - rz*qz - ry*qy - rx*qx;
296

  
297
      return new Static4D(tx,ty,tz,tw);
298
      }
299

  
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
// rotate 'vector' by quat^(-1)  ( i.e. return (quat^-1)*vector*quat )
302

  
303
    static Static4D rotateVectorByInvertedQuat(Static4D vector, Static4D quat)
304
      {
305
      float qx = quat.get1();
306
      float qy = quat.get2();
307
      float qz = quat.get3();
308
      float qw = quat.get4();
309

  
310
      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
311
      Static4D tmp = quatMultiply(quatInverted,vector);
312

  
313
      return quatMultiply(tmp,quat);
314
      }
315

  
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317
// rotate 'vector' by quat  ( i.e. return quat*vector*(quat^-1) )
318

  
319
    static Static4D rotateVectorByQuat(Static4D vector, Static4D quat)
320
      {
321
      float qx = quat.get1();
322
      float qy = quat.get2();
323
      float qz = quat.get3();
324
      float qw = quat.get4();
325

  
326
      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
327
      Static4D tmp = quatMultiply(quat,vector);
328

  
329
      return quatMultiply(tmp,quatInverted);
330
      }
331

  
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333

  
334
    private Static4D quatFromDrag(float dragX, float dragY)
335
      {
336
      float axisX = dragY;  // inverted X and Y - rotation axis is
337
      float axisY = dragX;  // perpendicular to (dragX,dragY)   Why not (-dragY, dragX) ? because Y axis is also inverted!
338
      float axisZ = 0;
339
      float axisL = (float)Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
340

  
341
      if( axisL>0 )
342
        {
343
        axisX /= axisL;
344
        axisY /= axisL;
345
        axisZ /= axisL;
346

  
347
        float cosA = (float)Math.cos(axisL*Math.PI/mScreenMin);
348
        float sinA = (float)Math.sqrt(1-cosA*cosA);
349

  
350
        return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
351
        }
352

  
353
      return new Static4D(0f, 0f, 0f, 1f);
354
      }
355

  
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357

  
358
    private boolean faceIsVisible(int face, float cubeHalfSize)
359
      {
360
      int sign = retFaceSign(face);
361
      int zAxis= retFaceZaxis(face);
362

  
363
      return sign*mCamera[zAxis] > cubeHalfSize;
364
      }
365

  
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

  
368
    private void convertTouchPointToScreenSpace(int x, int y)
369
      {
370
      float halfScrWidth  = mScreenWidth *0.5f;
371
      float halfScrHeight = mScreenHeight*0.5f;
372
      Static4D touchPoint = new Static4D(x-halfScrWidth, halfScrHeight-y, 0, 0);
373
      Static4D rotatedTouchPoint= rotateVectorByInvertedQuat(touchPoint, mQuatAccumulated);
374

  
375
      mPoint[0] = rotatedTouchPoint.get1();
376
      mPoint[1] = rotatedTouchPoint.get2();
377
      mPoint[2] = rotatedTouchPoint.get3();
378
      }
379

  
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

  
382
    private void convertCameraPointToScreenSpace()
383
      {
384
      Static4D cameraPoint = new Static4D(0, 0, mCameraDistance, 0);
385
      Static4D rotatedCamera= rotateVectorByInvertedQuat(cameraPoint, mQuatAccumulated);
386

  
387
      mCamera[0] = rotatedCamera.get1();
388
      mCamera[1] = rotatedCamera.get2();
389
      mCamera[2] = rotatedCamera.get3();
390
      }
391

  
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
394
// cast this touch point onto the surface defined by the 'face' and write the cast coords to 'output'.
395
// Center of the 'face' = (0,0), third coord always +- cubeHalfSize.
396

  
397
    private void castTouchPointOntoFace(int face, float cubeHalfSize, float[] output)
398
      {
399
      int sign = retFaceSign(face);
400
      int zAxis= retFaceZaxis(face);
401
      float diff = mPoint[zAxis]-mCamera[zAxis];
402

  
403
      float ratio =  diff!=0.0f ? (sign*cubeHalfSize-mCamera[zAxis])/diff : 0.0f;
404

  
405
      output[0] = (mPoint[0]-mCamera[0])*ratio + mCamera[0];
406
      output[1] = (mPoint[1]-mCamera[1])*ratio + mCamera[1];
407
      output[2] = (mPoint[2]-mCamera[2])*ratio + mCamera[2];
408
      }
409

  
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411

  
412
    private int retFaceSign(int face)
413
      {
414
      return (face==FRONT || face==RIGHT || face==TOP) ? 1:-1;
415
      }
416

  
417
///////////////////////////////////////////////////////////////////////////////////////////////////
418

  
419
    private int retFaceRotationSign(int face)
420
      {
421
      return (face==BACK || face==RIGHT || face==TOP) ? 1:-1;
422
      }
423

  
424
///////////////////////////////////////////////////////////////////////////////////////////////////
425
// retFace{X,Y,Z}axis: 3 functions which return which real AXIS gets mapped to which when we look
426
// directly at a given face. For example, when we look at the RIGHT face of the cube (with TOP still
427
// in the top) then the 'real' X axis becomes the 'Z' axis, thus retFaceZaxis(RIGHT) = VECTX.
428

  
429
    private int retFaceXaxis(int face)
430
      {
431
      switch(face)
432
        {
433
        case FRONT :
434
        case BACK  : return VECTX;
435
        case LEFT  :
436
        case RIGHT : return VECTZ;
437
        case TOP   :
438
        case BOTTOM: return VECTX;
49
        setRenderer(new RubikRenderer(this));
439 50
        }
440

  
441
      return -1;
442 51
      }
443 52

  
444 53
///////////////////////////////////////////////////////////////////////////////////////////////////
445 54

  
446
    private int retFaceYaxis(int face)
55
    RubikActivity getRubikActivity()
447 56
      {
448
      switch(face)
449
        {
450
        case FRONT :
451
        case BACK  : return VECTY;
452
        case LEFT  :
453
        case RIGHT : return VECTY;
454
        case TOP   :
455
        case BOTTOM: return VECTZ;
456
        }
457

  
458
      return -1;
459
      }
460

  
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462

  
463
    private int retFaceZaxis(int face)
464
      {
465
      switch(face)
466
        {
467
        case FRONT :
468
        case BACK  : return VECTZ;
469
        case LEFT  :
470
        case RIGHT : return VECTX;
471
        case TOP   :
472
        case BOTTOM: return VECTY;
473
        }
474

  
475
      return -1;
57
      return mWeakAct.get();
476 58
      }
477 59
}
478 60

  
src/main/res/layout/rubiklayout.xml
4 4
    android:layout_height="fill_parent"
5 5
    android:orientation="vertical" >
6 6

  
7
    <LinearLayout
8
        android:id="@+id/linearLayout"
9
        android:layout_width="fill_parent"
10
        android:layout_height="wrap_content"
11
        android:gravity="center|fill_horizontal" >
12

  
13
         <Button
14
            android:id="@+id/rubikCredits"
15
            android:layout_width="wrap_content"
16
            android:layout_height="64dp"
17
            android:layout_weight="0.5"
18
            android:onClick="Credits"
19
            android:paddingLeft="5dp"
20
            android:paddingRight="5dp"
21
            android:text="@string/credits" />
22

  
23
        <ImageButton
24
            android:id="@+id/rubikSize2"
25
            android:layout_width="64dp"
26
            android:layout_height="wrap_content"
27
            android:onClick="setSize"
28
            android:paddingLeft="5dp"
29
            android:paddingRight="5dp"
30
            android:src="@raw/button_rubik2"/>
31

  
32
        <ImageButton
33
            android:id="@+id/rubikSize3"
34
            android:layout_width="64dp"
35
            android:layout_height="wrap_content"
36
            android:onClick="setSize"
37
            android:paddingLeft="5dp"
38
            android:paddingRight="5dp"
39
            android:src="@raw/button_rubik3"/>
40

  
41
        <ImageButton
42
            android:id="@+id/rubikSize4"
43
            android:layout_width="64dp"
44
            android:layout_height="wrap_content"
45
            android:onClick="setSize"
46
            android:paddingLeft="5dp"
47
            android:paddingRight="5dp"
48
            android:src="@raw/button_rubik4"/>
49

  
50
        <Button
51
            android:id="@+id/rubikScramble"
52
            android:layout_width="wrap_content"
53
            android:layout_height="64dp"
54
            android:layout_weight="0.5"
55
            android:onClick="Scramble"
56
            android:paddingLeft="5dp"
57
            android:paddingRight="5dp"
58
            android:text="@string/scramble" />
59

  
60
    </LinearLayout>
61

  
62 7
    <org.distorted.examples.rubik.RubikSurfaceView
63 8
        android:id="@+id/rubikSurfaceView"
64 9
        android:layout_width="fill_parent"
65 10
        android:layout_height="0dp"
66 11
        android:layout_weight="1" />
67 12

  
13
    <TextView
14
        android:id="@+id/rubikText"
15
        android:layout_width="fill_parent"
16
        android:layout_height="48dp"
17
        android:gravity="center_vertical|center"
18
        android:textAppearance="?android:attr/textAppearanceMedium" />
19

  
68 20
</LinearLayout>
src/main/res/values/strings.xml
113 113
    <string name="glow_alpha_placeholder">Alpha: %1$.2f</string>
114 114
    <string name="inflate_placeholder">Inflate: %1$.2f</string>
115 115
    <string name="effect_id_placeholder">ID: %1$d</string>
116
    <string name="rubik_placeholder">Cube: %1$d Available Memory: %2$d MB</string>
116 117

  
117 118
    <string name="example_monalisa">Mona Lisa</string>
118 119
    <string name="example_monalisa_subtitle">The basics of Distortions.</string>
......
184 185
    <string name="example_moving_glow_subtitle">See moving objects glowing with light.</string>
185 186
    <string name="example_earth">Earth</string>
186 187
    <string name="example_earth_subtitle">Test the sphere Mesh by showing the Earth in cosmos.</string>
187
    <string name="example_rubik">Rubik Cube</string>
188
    <string name="example_rubik_subtitle">Moveable Rubik Cube.</string>
188
    <string name="example_rubik">Memory Test</string>
189
    <string name="example_rubik_subtitle">Keep an eye on memory consumption while allocating and deallocating Nodes.</string>
189 190

  
190 191
    <string name="example_movingeffects_toast">Click on \'RESET\' and define your path by touching the screen. Then click on one of the effects and see it move along your path.</string>
191 192
    <string name="example_rotate_toast">Rotate the scene by swiping the screen</string>

Also available in: Unified diff