Project

General

Profile

« Previous | Next » 

Revision beb325a0

Added by Leszek Koltunski almost 5 years ago

Major restructuring - separate the Manipulated Objects (i.e. at the time being - Cubes of various sizes) and the class holding knowledge how those Objects move ( RubikCubeMovement ) into a separate package; remove all knowledge of Objects and the way they move from the main package.

View differences:

src/main/java/org/distorted/effect/BaseEffect.java
19 19

  
20 20
package org.distorted.effect;
21 21

  
22
import java.lang.reflect.InvocationTargetException;
22 23
import java.lang.reflect.Method;
23 24
import android.content.SharedPreferences;
24 25

  
......
195 196

  
196 197
  ////////////////////////////////////////////////////////////////////////////////
197 198

  
198
    public long startEffect(RubikRenderer renderer)
199
    public long startEffect(RubikRenderer renderer) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
199 200
      {
200
      Method method1, method2;
201
      BaseEffect baseEffect=null;
201
      Method method1 = mClass.getDeclaredMethod("create", int.class);
202 202

  
203
      try
204
        {
205
        method1 = mClass.getDeclaredMethod("create", int.class);
206
        }
207
      catch(NoSuchMethodException ex)
208
        {
209
        android.util.Log.e("BaseEffect", mClass.getSimpleName()+": 1 exception getting method: "+ex.getMessage());
210
        return -1;
211
        }
212

  
213
      try
214
        {
215
        if( method1!=null )
216
          {
217
          Object value = method1.invoke(null,mCurrentType);
218
          baseEffect = (BaseEffect)value;
219
          }
220
        }
221
      catch(Exception ex)
222
        {
223
        android.util.Log.e("BaseEffect", mClass.getSimpleName()+": 1 exception invoking method: "+ex.getMessage());
224
        return -2;
225
        }
203
      Object value1 = method1.invoke(null,mCurrentType);
204
      BaseEffect baseEffect = (BaseEffect)value1;
226 205

  
227
      try
228
        {
229
        method2 = mClass.getDeclaredMethod("start", int.class, RubikRenderer.class);
230
        }
231
      catch(NoSuchMethodException ex)
232
        {
233
        android.util.Log.e("BaseEffect", mClass.getSimpleName()+": 2 exception getting method: "+ex.getMessage());
234
        return -3;
235
        }
236

  
237
      try
238
        {
239
        if( method2!=null )
240
          {
241
          Integer translated = translatePos(mCurrentPos)+1;
242
          Object value = method2.invoke(baseEffect,translated,renderer);
243
          return (Long)value;
244
          }
245
        }
246
      catch(Exception ex)
247
        {
248
        android.util.Log.e("BaseEffect", mClass.getSimpleName()+": 2 exception invoking method: "+ex.getMessage());
249
        }
206
      Method method2 = mClass.getDeclaredMethod("start", int.class, RubikRenderer.class);
250 207

  
251
      return -4;
208
      Integer translated = translatePos(mCurrentPos)+1;
209
      Object value2 = method2.invoke(baseEffect,translated,renderer);
210
      return (Long)value2;
252 211
      }
253 212

  
254 213
  ////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/effect/scramble/ScrambleEffect.java
23 23
import org.distorted.library.effect.Effect;
24 24
import org.distorted.library.main.DistortedEffects;
25 25
import org.distorted.library.message.EffectListener;
26
import org.distorted.magic.RubikCube;
26
import org.distorted.object.RubikCube;
27 27
import org.distorted.magic.RubikRenderer;
28 28

  
29 29
import java.lang.reflect.Method;
......
304 304
    mCube     = renderer.getCube();
305 305
    mListener = renderer;
306 306

  
307
    mCube.solve();
308

  
307 309
    int numScrambles = renderer.getNumScrambles();
308 310
    int dura = (int)(duration*Math.pow(numScrambles,0.6f));
309 311
    createBaseEffects(dura,numScrambles);
src/main/java/org/distorted/effect/sizechange/SizeChangeEffect.java
24 24
import org.distorted.library.main.DistortedEffects;
25 25
import org.distorted.library.main.DistortedScreen;
26 26
import org.distorted.library.message.EffectListener;
27
import org.distorted.magic.RubikCube;
27
import org.distorted.object.RubikCube;
28 28
import org.distorted.magic.RubikRenderer;
29 29

  
30 30
import java.lang.reflect.Method;
src/main/java/org/distorted/effect/solve/SolveEffect.java
24 24
import org.distorted.library.main.DistortedEffects;
25 25
import org.distorted.library.main.DistortedScreen;
26 26
import org.distorted.library.message.EffectListener;
27
import org.distorted.magic.RubikCube;
27
import org.distorted.object.RubikCube;
28 28
import org.distorted.magic.RubikRenderer;
29 29

  
30 30
import java.lang.reflect.Method;
src/main/java/org/distorted/effect/win/WinEffect.java
24 24
import org.distorted.library.main.DistortedEffects;
25 25
import org.distorted.library.main.DistortedScreen;
26 26
import org.distorted.library.message.EffectListener;
27
import org.distorted.magic.RubikCube;
27
import org.distorted.object.RubikCube;
28 28
import org.distorted.magic.RubikRenderer;
29 29

  
30 30
import java.lang.reflect.Method;
src/main/java/org/distorted/magic/RubikActivity.java
20 20
package org.distorted.magic;
21 21

  
22 22
import android.content.SharedPreferences;
23
import android.graphics.PorterDuff;
24
import android.graphics.drawable.Drawable;
25 23
import android.opengl.GLSurfaceView;
26 24
import android.os.Bundle;
27 25
import android.preference.PreferenceManager;
28
import android.support.v4.content.ContextCompat;
29 26
import android.support.v7.app.AppCompatActivity;
30
import android.util.DisplayMetrics;
31 27
import android.view.View;
32
import android.view.ViewGroup;
33
import android.widget.ImageButton;
34
import android.widget.LinearLayout;
35 28

  
36 29
import org.distorted.component.HorizontalNumberPicker;
37 30
import org.distorted.library.main.DistortedLibrary;
......
45 38
    public static final int DEF_SCRAMBLE =  1;
46 39
    public static final int MAX_SCRAMBLE = 18;
47 40

  
48
    private static int mButton = RubikSize.SIZE3.ordinal();
49 41
    private HorizontalNumberPicker mPicker;
50 42

  
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52

  
53
    private void markButton(int button)
54
      {
55
      mButton = button;
56

  
57
      for(int b=0; b<RubikSize.LENGTH; b++)
58
        {
59
        Drawable d = findViewById(b).getBackground();
60

  
61
        if( b==button )
62
          {
63
          d.setColorFilter(ContextCompat.getColor(this,R.color.red), PorterDuff.Mode.MULTIPLY);
64
          }
65
        else
66
          {
67
          d.clearColorFilter();
68
          }
69
        }
70
      }
71

  
72 43
///////////////////////////////////////////////////////////////////////////////////////////////////
73 44

  
74 45
    private void savePreferences()
......
102 73
      mPicker.setValue(scramble);
103 74
      }
104 75

  
105
///////////////////////////////////////////////////////////////////////////////////////////////////
106

  
107
    private void addSizeButtons()
108
      {
109
      LinearLayout layout = findViewById(R.id.sizeLayout);
110
      DisplayMetrics metrics = getResources().getDisplayMetrics();
111
      float scale = metrics.density;
112
      int size = (int)(64*scale +0.5f);
113
      int padding = (int)(3*scale + 0.5f);
114
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
115

  
116
      for(int i=0; i<RubikSize.LENGTH; i++)
117
        {
118
        ImageButton button = new ImageButton(this);
119
        button.setLayoutParams(params);
120
        button.setId(i);
121
        button.setPadding(padding,0,padding,0);
122
        int iconID = RubikSize.getSize(i).getIconID();
123
        button.setImageResource(iconID);
124
        button.setOnClickListener(this);
125
        layout.addView(button);
126
        }
127
      }
128

  
129
///////////////////////////////////////////////////////////////////////////////////////////////////
130

  
131
    static int getRedButton()
132
      {
133
      return mButton;
134
      }
135

  
136 76
///////////////////////////////////////////////////////////////////////////////////////////////////
137 77

  
138 78
    @Override
......
141 81
      super.onCreate(savedState);
142 82
      setTheme(R.style.CustomActivityThemeNoActionBar);
143 83
      setContentView(R.layout.main);
144
      addSizeButtons();
145
      markButton(mButton);
84
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
85
      view.addSizeButtons(this);
86
      view.markButton(view.getRedButton());
146 87

  
147 88
      mPicker = findViewById(R.id.rubikNumberPicker);
148 89
      mPicker.setMin(MIN_SCRAMBLE);
......
199 140

  
200 141
        if( success )
201 142
          {
202
          markButton(id);
143
          view.markButton(id);
203 144
          }
204 145
        }
205 146
      }
......
218 159

  
219 160
    public void Scores(View v)
220 161
      {
162
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
221 163
      Bundle bundle = new Bundle();
222
      bundle.putInt("tab", mButton);
164
      bundle.putInt("tab", view.getRedButton());
223 165

  
224 166
      RubikScores scores = new RubikScores();
225 167
      scores.setArguments(bundle);
......
242 184

  
243 185
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
244 186
      view.getRenderer().scrambleCube(scramble);
187
      view.enterScrambleMode();
245 188
      }
246 189

  
247 190
///////////////////////////////////////////////////////////////////////////////////////////////////
......
250 193
      {
251 194
      RubikSurfaceView view = findViewById(R.id.rubikSurfaceView);
252 195
      view.getRenderer().solveCube();
196
      view.leaveScrambleMode();
253 197
      }
254 198
}
src/main/java/org/distorted/magic/RubikCube.java
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.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.DistortedTexture;
35
import org.distorted.library.mesh.MeshCubes;
36
import org.distorted.library.mesh.MeshFlat;
37
import org.distorted.library.message.EffectListener;
38
import org.distorted.library.type.Dynamic1D;
39
import org.distorted.library.type.Static1D;
40
import org.distorted.library.type.Static3D;
41
import org.distorted.library.type.Static4D;
42

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

  
45
public class RubikCube extends DistortedNode
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
    public static final int VECTX = 0;  //
55
    public static final int VECTY = 1;  // don't change this
56
    public static final int VECTZ = 2;  //
57

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

  
71
    private int mRotAxis, mRotRow;
72
    private int mSize;
73

  
74
    private DistortedTexture mNodeTexture;
75

  
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77

  
78
    RubikCube(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture texture, MeshFlat mesh, DistortedEffects effects)
79
      {
80
      super(texture,effects,mesh);
81

  
82
      mNodeTexture = texture;
83

  
84
      mSize = size;
85

  
86
      mRotationAngleStatic = new Static1D(0);
87
      mRotationAngleMiddle = new Static1D(0);
88
      mRotationAngleFinal  = new Static1D(0);
89

  
90
      mMove     = new Static3D(0,0,0);
91
      mScale    = new Static3D(1,1,1);
92
      mNodeMove = new Static3D(0,0,0);
93
      mNodeScale= new Static3D(1,1,1);
94

  
95
      mQuatAccumulated = quatAcc;
96

  
97
      mRotAxis = VECTX;
98
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
99

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

  
109
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
110

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

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

  
121
      MatrixEffectMove       nodeMoveEffect  = new MatrixEffectMove(mNodeMove);
122
      MatrixEffectScale      nodeScaleEffect = new MatrixEffectScale(mNodeScale);
123

  
124
      effects.apply(nodeScaleEffect);
125
      effects.apply(nodeMoveEffect);
126

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

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

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

  
145
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
146

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

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

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

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

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

  
184
              attach(mNodes[x][y][z]);
185
              }
186
            }
187
      }
188

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

  
191
    private int computeNearestAngle(float angle)
192
      {
193
      final int NEAREST = 90;
194

  
195
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
196
      if( angle< -(NEAREST/2) ) tmp-=1;
197

  
198
      return NEAREST*tmp;
199
      }
200

  
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202
// All legal rotation quats must have all four of their components equal to either
203
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2.
204
//
205
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
206
// correct the value of the 'scramble' quat to what it should be.
207
//
208
// We also have to remember that the group of unit quaternions is a double-cover of rotations
209
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
210

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

  
214
    private void normalizeScrambleQuat(int i, int j, int k)
215
      {
216
      Static4D quat = mQuatScramble[i][j][k];
217

  
218
      float x = quat.get1();
219
      float y = quat.get2();
220
      float z = quat.get3();
221
      float w = quat.get4();
222
      float diff;
223

  
224
      for(float legal: LEGAL)
225
        {
226
        diff = x-legal;
227
        if( diff*diff<0.01f ) x = legal;
228
        diff = y-legal;
229
        if( diff*diff<0.01f ) y = legal;
230
        diff = z-legal;
231
        if( diff*diff<0.01f ) z = legal;
232
        diff = w-legal;
233
        if( diff*diff<0.01f ) w = legal;
234
        }
235

  
236
      if( w<0 )
237
        {
238
        w = -w;
239
        z = -z;
240
        y = -y;
241
        x = -x;
242
        }
243
      else if( w==0 )
244
        {
245
        if( z<0 )
246
          {
247
          z = -z;
248
          y = -y;
249
          x = -x;
250
          }
251
        else if( z==0 )
252
          {
253
          if( y<0 )
254
            {
255
            y = -y;
256
            x = -x;
257
            }
258
          else if( y==0 )
259
            {
260
            if( x<0 )
261
              {
262
              x = -x;
263
              }
264
            }
265
          }
266
        }
267

  
268
      mQuatScramble[i][j][k].set(x,y,z,w);
269
      }
270

  
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

  
273
    private float getSinkStrength()
274
      {
275
      switch(mSize)
276
        {
277
        case 1 : return 1.1f;
278
        case 2 : return 1.5f;
279
        case 3 : return 1.8f;
280
        case 4 : return 2.0f;
281
        default: return 3.0f - 4.0f/mSize;
282
        }
283
      }
284

  
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

  
287
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
288
      {
289
      switch(vector)
290
        {
291
        case VECTX: return mCurrentPosition[x][y][z].get1()==row;
292
        case VECTY: return mCurrentPosition[x][y][z].get2()==row;
293
        case VECTZ: return mCurrentPosition[x][y][z].get3()==row;
294
        }
295

  
296
      return false;
297
      }
298

  
299
///////////////////////////////////////////////////////////////////////////////////////////////////
300

  
301
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
302
      {
303
      Static3D current = mCurrentPosition[x][y][z];
304
      float diff = 0.5f*(mSize-1);
305
      float cubitCenterX = current.get1() - diff;
306
      float cubitCenterY = current.get2() - diff;
307
      float cubitCenterZ = current.get3() - diff;
308

  
309
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
310
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
311

  
312
      float rotatedX = rotatedCenter.get1() + diff;
313
      float rotatedY = rotatedCenter.get2() + diff;
314
      float rotatedZ = rotatedCenter.get3() + diff;
315

  
316
      int roundedX = (int)(rotatedX+0.1f);
317
      int roundedY = (int)(rotatedY+0.1f);
318
      int roundedZ = (int)(rotatedZ+0.1f);
319

  
320
      mCurrentPosition[x][y][z].set1(roundedX);
321
      mCurrentPosition[x][y][z].set2(roundedY);
322
      mCurrentPosition[x][y][z].set3(roundedZ);
323
      }
324

  
325
///////////////////////////////////////////////////////////////////////////////////////////////////
326

  
327
    boolean isSolved()
328
      {
329
      Static4D q = mQuatScramble[0][0][0];
330

  
331
      float x = q.get1();
332
      float y = q.get2();
333
      float z = q.get3();
334
      float w = q.get4();
335

  
336
      for(int i = 0; i< mSize; i++)
337
        for(int j = 0; j< mSize; j++)
338
          for(int k = 0; k< mSize; k++)
339
            {
340
            if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 )
341
              {
342
              q = mQuatScramble[i][j][k];
343

  
344
              if( q.get1()!=x || q.get2()!=y || q.get3()!=z || q.get4()!=w )
345
                {
346
                return false;
347
                }
348
              }
349
            }
350

  
351
      return true;
352
      }
353

  
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
356
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
357
// memory leak.
358

  
359
    void releaseResources()
360
      {
361
      mTexture.markForDeletion();
362

  
363
      for(int x=0; x<mSize; x++)
364
        for(int y=0; y<mSize; y++)
365
          for(int z=0; z<mSize; z++)
366
            {
367
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
368
              {
369
              mCubes[x][y][z].markForDeletion();
370
              mNodes[x][y][z].markForDeletion();
371
              }
372
            }
373
      }
374

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

  
377
    void addNewRotation(int vector, int row )
378
      {
379
      Static3D axis = VectX;
380

  
381
      switch(vector)
382
        {
383
        case VECTX: axis = VectX; break;
384
        case VECTY: axis = VectY; break;
385
        case VECTZ: axis = VectZ; break;
386
        }
387

  
388
      mRotAxis = vector;
389
      mRotRow  = row;
390

  
391
      mRotationAngleStatic.set1(0.0f);
392

  
393
      for(int x=0; x<mSize; x++)
394
        for(int y=0; y<mSize; y++)
395
          for(int z=0; z<mSize; z++)
396
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
397
              {
398
              if( belongsToRotation(x,y,z,vector,mRotRow) )
399
                {
400
                mRotationAxis[x][y][z].set(axis);
401
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
402
                }
403
              }
404
      }
405

  
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407

  
408
    long finishRotationNow(EffectListener listener)
409
      {
410
      boolean first = true;
411
      long effectID=0;
412

  
413
      for(int x=0; x<mSize; x++)
414
        for(int y=0; y<mSize; y++)
415
          for(int z=0; z<mSize; z++)
416
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
417
              {
418
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
419
                {
420
                if( first )
421
                  {
422
                  first = false;
423
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
424
                  effectID = mRotateEffect[x][y][z].getID();
425
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
426

  
427
                  if( pointNum>=1 )
428
                    {
429
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
430
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
431
                    mRotationAngleStatic.set1(startingAngle);
432
                    mRotationAngleFinal.set1(nearestAngleInDegrees);
433
                    mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
434
                    }
435
                  else
436
                    {
437
                    android.util.Log.e("cube", "ERROR finishing rotation!");
438
                    return 0;
439
                    }
440
                  }
441

  
442
                mRotationAngle[x][y][z].setDuration(POST_ROTATION_MILLISEC);
443
                mRotationAngle[x][y][z].resetToBeginning();
444
                mRotationAngle[x][y][z].removeAll();
445
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
446
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
447
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
448
                }
449
              }
450

  
451
      return effectID;
452
      }
453

  
454
///////////////////////////////////////////////////////////////////////////////////////////////////
455

  
456
    void continueRotation(float angleInDegrees)
457
      {
458
      mRotationAngleStatic.set1(angleInDegrees);
459
      }
460

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

  
463
    void createTexture()
464
      {
465
      Bitmap bitmap;
466

  
467
      final int S = 128;
468
      final int W = 3*S;
469
      final int H = 2*S;
470
      final int R = S/10;
471
      final int M = S/20;
472

  
473
      Paint paint = new Paint();
474
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
475
      Canvas canvas = new Canvas(bitmap);
476

  
477
      paint.setAntiAlias(true);
478
      paint.setTextAlign(Paint.Align.CENTER);
479
      paint.setStyle(Paint.Style.FILL);
480

  
481
      // 3x2 bitmap = 6 squares:
482
      //
483
      // RED     GREEN   BLUE
484
      // YELLOW  WHITE   BROWN
485

  
486
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
487
      canvas.drawRect(0, 0, W, H, paint);                          //
488

  
489
      paint.setColor(0xffff0000);                                  // RED
490
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
491
      paint.setColor(0xff00ff00);                                  // GREEN
492
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
493
      paint.setColor(0xff0000ff);                                  // BLUE
494
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
495
      paint.setColor(0xffffff00);                                  // YELLOW
496
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
497
      paint.setColor(0xffffffff);                                  // WHITE
498
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
499
      paint.setColor(0xffb5651d);                                  // BROWN
500
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
501

  
502
      mTexture.setTexture(bitmap);
503
      }
504

  
505
///////////////////////////////////////////////////////////////////////////////////////////////////
506

  
507
    void recomputeScaleFactor(int screenWidth, int screenHeight, float size)
508
      {
509
      int texW = mNodeTexture.getWidth();
510
      int texH = mNodeTexture.getHeight();
511

  
512
      if( (float)texH/texW > (float)screenHeight/screenWidth )
513
        {
514
        int w = (screenHeight*texW)/texH;
515
        float factor = (float)screenHeight/texH;
516
        mNodeMove.set((screenWidth-w)*0.5f ,0, 0);
517
        mNodeScale.set(factor,factor,factor);
518
        }
519
      else
520
        {
521
        int h = (screenWidth*texH)/texW;
522
        float factor = (float)screenWidth/texW;
523
        mNodeMove.set(0,(screenHeight-h)*0.5f,0);
524
        mNodeScale.set(factor,factor,factor);
525
        }
526

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

  
529
      mMove.set( texW*0.5f , texH*0.5f , 0.0f );
530
      mScale.set(scaleFactor,scaleFactor,scaleFactor);
531
      }
532

  
533
///////////////////////////////////////////////////////////////////////////////////////////////////
534
// PUBLIC API
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536

  
537
    public Static4D getRotationQuat()
538
      {
539
      return mQuatAccumulated;
540
      }
541

  
542
///////////////////////////////////////////////////////////////////////////////////////////////////
543

  
544
    public void apply(Effect effect, int position)
545
      {
546
      for(int x=0; x<mSize; x++)
547
        for(int y=0; y<mSize; y++)
548
          for(int z=0; z<mSize; z++)
549
            {
550
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
551
              {
552
              mEffects[x][y][z].apply(effect, position);
553
              }
554
            }
555
      }
556

  
557
///////////////////////////////////////////////////////////////////////////////////////////////////
558

  
559
    public void remove(long effectID)
560
      {
561
      for(int x=0; x<mSize; x++)
562
        for(int y=0; y<mSize; y++)
563
          for(int z=0; z<mSize; z++)
564
            {
565
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
566
              {
567
              mEffects[x][y][z].abortById(effectID);
568
              }
569
            }
570
      }
571

  
572
///////////////////////////////////////////////////////////////////////////////////////////////////
573

  
574
    public void solve()
575
      {
576
      for(int x=0; x<mSize; x++)
577
        for(int y=0; y<mSize; y++)
578
          for(int z=0; z<mSize; z++)
579
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
580
              {
581
              mQuatScramble[x][y][z].set(0,0,0,1);
582
              mCurrentPosition[x][y][z].set(x,y,z);
583
              }
584
      }
585

  
586
///////////////////////////////////////////////////////////////////////////////////////////////////
587

  
588
    public int getSize()
589
      {
590
      return mSize;
591
      }
592

  
593
///////////////////////////////////////////////////////////////////////////////////////////////////
594

  
595
    public long addNewRotation(int vector, int row, int angle, long durationMillis, EffectListener listener )
596
      {
597
      Static3D axis = VectX;
598
      long effectID=0;
599
      boolean first = true;
600

  
601
      switch(vector)
602
        {
603
        case VECTX: axis = VectX; break;
604
        case VECTY: axis = VectY; break;
605
        case VECTZ: axis = VectZ; break;
606
        }
607

  
608
      mRotAxis = vector;
609
      mRotRow  = row;
610

  
611
      mRotationAngleStatic.set1(0.0f);
612

  
613
      for(int x=0; x<mSize; x++)
614
        for(int y=0; y<mSize; y++)
615
          for(int z=0; z<mSize; z++)
616
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
617
              {
618
              if( belongsToRotation(x,y,z,vector,mRotRow) )
619
                {
620
                mRotationAxis[x][y][z].set(axis);
621
                mRotationAngle[x][y][z].setDuration(durationMillis);
622
                mRotationAngle[x][y][z].resetToBeginning();
623
                mRotationAngle[x][y][z].add(new Static1D(0));
624
                mRotationAngle[x][y][z].add(new Static1D(angle));
625

  
626
                if( first )
627
                  {
628
                  first = false;
629
                  effectID = mRotateEffect[x][y][z].getID();
630
                  mRotateEffect[x][y][z].notifyWhenFinished(listener);
631
                  }
632
                }
633
              }
634

  
635
      return effectID;
636
      }
637

  
638
///////////////////////////////////////////////////////////////////////////////////////////////////
639

  
640
    public void removeRotationNow()
641
      {
642
      float qx=0,qy=0,qz=0;
643
      boolean first = true;
644
      Static4D quat = null;
645

  
646
      switch(mRotAxis)
647
        {
648
        case VECTX: qx=1; break;
649
        case VECTY: qy=1; break;
650
        case VECTZ: qz=1; break;
651
        }
652

  
653
      for(int x=0; x<mSize; x++)
654
        for(int y=0; y<mSize; y++)
655
          for(int z=0; z<mSize; z++)
656
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
657
              {
658
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
659
                {
660
                if( first )
661
                  {
662
                  first = false;
663
                  int pointNum = mRotationAngle[x][y][z].getNumPoints();
664

  
665
                  if( pointNum>=1 )
666
                    {
667
                    float startingAngle = mRotationAngle[x][y][z].getPoint(pointNum-1).get1();
668
                    int nearestAngleInDegrees = computeNearestAngle(startingAngle);
669
                    double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
670
                    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
671
                    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
672
                    quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
673
                    }
674
                  else
675
                    {
676
                    android.util.Log.e("cube", "ERROR removing rotation!");
677
                    return;
678
                    }
679
                  }
680

  
681
                mRotationAngle[x][y][z].removeAll();
682
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
683
                normalizeScrambleQuat(x,y,z);
684
                modifyCurrentPosition(x,y,z,quat);
685
                }
686
              }
687

  
688
      mRotationAngleStatic.set1(0);
689
      }
690
}
src/main/java/org/distorted/magic/RubikRenderer.java
29 29
import org.distorted.library.main.DistortedTexture;
30 30
import org.distorted.library.mesh.MeshFlat;
31 31
import org.distorted.library.message.EffectListener;
32
import org.distorted.object.RubikCube;
33
import org.distorted.object.RubikCubeMovement;
32 34

  
33 35
import javax.microedition.khronos.egl.EGLConfig;
34 36
import javax.microedition.khronos.opengles.GL10;
......
37 39

  
38 40
public class RubikRenderer implements GLSurfaceView.Renderer, EffectListener
39 41
{
40
    private static final float CUBE_SCREEN_RATIO = 0.5f;
41 42
    private static final float CAMERA_DISTANCE   = 0.6f;  // 0.6 of the length of max(scrHeight,scrWidth)
42 43
    public  static final int TEXTURE_SIZE = 600;
43 44

  
44 45
    private RubikSurfaceView mView;
45 46
    private DistortedScreen mScreen;
46
    private float mCubeSizeInScreenSpace;
47 47
    private int mNextCubeSize, mScrambleCubeNum;
48 48
    private long mRotationFinishedID;
49 49
    private long[] mEffectID;
......
60 60
    RubikRenderer(RubikSurfaceView v)
61 61
      {
62 62
      mView = v;
63

  
64 63
      mScreen = new DistortedScreen();
65 64

  
66 65
      mOldCube = null;
......
84 83
      mEffectID = new long[BaseEffect.Type.LENGTH];
85 84

  
86 85
      mMesh= new MeshFlat(20,20);
87
      mNextCubeSize = RubikSize.getSize(RubikActivity.getRedButton()).getCubeSize();
86
      mNextCubeSize = RubikSize.getSize(mView.getRedButton()).getCubeSize();
88 87
      }
89 88

  
90 89
///////////////////////////////////////////////////////////////////////////////////////////////////
......
95 94
     return (float)(2*halfFOVInRadians*(180/Math.PI));
96 95
     }
97 96

  
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

  
100
   private void recomputeScaleFactor(int screenWidth, int screenHeight)
101
     {
102
     mCubeSizeInScreenSpace = CUBE_SCREEN_RATIO*(screenWidth>screenHeight ? screenHeight:screenWidth);
103

  
104
     if( mNewCube!=null )
105
       {
106
       mNewCube.recomputeScaleFactor(screenWidth, screenHeight, mCubeSizeInScreenSpace);
107
       }
108
     }
109

  
110 97
///////////////////////////////////////////////////////////////////////////////////////////////////
111 98

  
112 99
   private void createCubeNow(int newSize)
......
122 109

  
123 110
     if( mScreenWidth!=0 )
124 111
       {
125
       recomputeScaleFactor(mScreenWidth,mScreenHeight);
112
       mNewCube.recomputeScaleFactor(mScreenWidth, mScreenHeight);
126 113
       }
127 114

  
115
     RubikCubeMovement movement = new RubikCubeMovement(mNewCube);
116
     mView.setMovement(movement);
117

  
128 118
     mIsSolved = true;
129 119
     }
130 120

  
......
135 125
     {
136 126
     int index = type.ordinal();
137 127

  
138
     mEffectID[index] = type.startEffect(this);
139

  
140
     if( mEffectID[index] < 0 )
128
     try
129
       {
130
       mEffectID[index] = type.startEffect(this);
131
       }
132
     catch( Exception ex )
141 133
       {
134
       android.util.Log.e("renderer", "exception starting effect: "+ex.getMessage());
135

  
142 136
       mCanUI     = true;
143 137
       mCanRotate = true;
144 138
       mCanDrag   = true;
......
188 182
       }
189 183
     }
190 184

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

  
193
   float returnCubeSizeInScreenSpace()
194
     {
195
     return mCubeSizeInScreenSpace;
196
     }
197

  
198 185
///////////////////////////////////////////////////////////////////////////////////////////////////
199 186

  
200 187
   boolean canRotate()
......
267 254
         mCanRotate      = false;
268 255
         mCanUI          = false;
269 256
         doEffectNow( BaseEffect.Type.WIN );
257
         mView.leaveScrambleModeNonUI();
270 258
         }
271 259
       else
272 260
         {
......
316 304
      float cameraDistance = CAMERA_DISTANCE*(width>height ? width:height);
317 305
      float fovInDegrees   = computeFOV(cameraDistance,height);
318 306

  
319
      mScreen.setProjection( fovInDegrees, 0.1f);
320 307
      mView.setScreenSize(width,height);
321 308
      mView.setCameraDist(cameraDistance);
309

  
310
      mScreen.setProjection( fovInDegrees, 0.1f);
322 311
      mScreen.resize(width, height);
323 312

  
324
      recomputeScaleFactor(width,height);
313
      if( mNewCube!=null )
314
        {
315
        mNewCube.recomputeScaleFactor(width, height);
316
        }
325 317

  
326 318
      mScreenHeight = height;
327 319
      mScreenWidth  = width;
src/main/java/org/distorted/magic/RubikScoresDownloader.java
211 211
        }
212 212
      catch( final Exception e)
213 213
        {
214
        mReceiver.exception("biffed it getting HTTPResponse");
214
        mReceiver.exception("Failed to get an answer from the High Scores server");
215 215
        return false;
216 216
        }
217 217
      }
src/main/java/org/distorted/magic/RubikScoresPagerAdapter.java
42 42

  
43 43
    int c = mViewPager.getCurrentItem();
44 44

  
45
    addPage(c,country[c],name[c],time[c]);
45
    addPage(mViews[c],country[c],name[c],time[c]);
46 46

  
47 47
    for(int i=0; i<RubikSize.LENGTH; i++)
48 48
      {
49 49
      if( i==c ) continue;
50 50

  
51
      addPage(i,country[i],name[i],time[i]);
51
      addPage(mViews[i],country[i],name[i],time[i]);
52 52
      }
53 53
    }
54 54

  
......
79 79

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

  
82
  private void addPage(final int page, final int[][] country, final String[][] name, final String[][] time)
82
  private void addPage(final RubikScoresView view, final int[][] country, final String[][] name, final String[][] time)
83 83
    {
84 84
    for(int section=0; section<RubikActivity.MAX_SCRAMBLE; section++)
85 85
      {
......
93 93
        @Override
94 94
        public void run()
95 95
          {
96
          mViews[page].addSection(mAct, sec, c, n, t);
96
          view.addSection(mAct, sec, c, n, t);
97 97
          }
98 98
        });
99 99

  
src/main/java/org/distorted/magic/RubikScoresView.java
26 26
import android.widget.FrameLayout;
27 27
import android.widget.ImageView;
28 28
import android.widget.LinearLayout;
29
import android.widget.ScrollView;
30 29
import android.widget.TextView;
31 30

  
32 31
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/magic/RubikSurfaceView.java
22 22
import android.app.ActivityManager;
23 23
import android.content.Context;
24 24
import android.content.pm.ConfigurationInfo;
25
import android.graphics.PorterDuff;
26
import android.graphics.drawable.Drawable;
25 27
import android.opengl.GLSurfaceView;
28
import android.support.v4.content.ContextCompat;
26 29
import android.util.AttributeSet;
30
import android.util.DisplayMetrics;
27 31
import android.view.MotionEvent;
32
import android.view.ViewGroup;
33
import android.widget.Button;
34
import android.widget.ImageButton;
35
import android.widget.LinearLayout;
28 36

  
29 37
import org.distorted.library.type.Static4D;
38
import org.distorted.object.RubikCubeMovement;
30 39

  
31 40
///////////////////////////////////////////////////////////////////////////////////////////////////
32 41

  
33
class RubikSurfaceView extends GLSurfaceView
42
public class RubikSurfaceView extends GLSurfaceView
34 43
{
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 44
    // Moving the finger by 1/12 the distance of min(scrWidth,scrHeight) will start a Rotation.
40 45
    private final static int ROTATION_SENSITIVITY =  12;
41 46

  
42 47
    // Every 1/12 the distance of min(scrWidth,scrHeight) the direction of cube rotation will reset.
43 48
    private final static int DIRECTION_SENSITIVITY=  12;
44 49

  
45
    private final static int NONE   =-1;
46
    private final static int FRONT  = 0;  // has to be 6 consecutive ints
47
    private final static int BACK   = 1;  // FRONT ... BOTTOM
48
    private final static int LEFT   = 2;  //
49
    private final static int RIGHT  = 3;  //
50
    private final static int TOP    = 4;  //
51
    private final static int BOTTOM = 5;  //
50
    private RubikRenderer mRenderer;
51
    private RubikCubeMovement mMovement;
52 52

  
53
    private static final int[] VECT = {RubikCube.VECTX,RubikCube.VECTY,RubikCube.VECTZ};
53
    private boolean mInScrambleMode;
54
    private int mButton = RubikSize.SIZE3.ordinal();
54 55

  
55 56
    private boolean mDragging, mBeginningRotation, mContinuingRotation;
56 57
    private int mX, mY;
57
    private int mRotationVect;
58
    private RubikRenderer mRenderer;
59

  
60
    private float[] mPoint, mCamera, mTouchPointCastOntoFace, mDiff, mTouchPoint; // all in screen space
61 58
    private int mLastTouchedFace;
62
    private int mScreenWidth, mScreenHeight, mScreenMin;
63 59
    private float mCameraDistance;
60
    private int mScreenWidth, mScreenHeight, mScreenMin;
64 61

  
65 62
    private static Static4D mQuatCurrent    = new Static4D(0,0,0,1);
66 63
    private static Static4D mQuatAccumulated= new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
......
70 67
///////////////////////////////////////////////////////////////////////////////////////////////////
71 68
// return quat1*quat2
72 69

  
73
    static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
70
    public static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
74 71
      {
75 72
      float qx = quat1.get1();
76 73
      float qy = quat1.get2();
......
91 88
      }
92 89

  
93 90
///////////////////////////////////////////////////////////////////////////////////////////////////
94
// rotate 'vector' by quat^(-1)  ( i.e. return (quat^-1)*vector*quat )
91
// rotate 'vector' by quat  ( i.e. return quat*vector*(quat^-1) )
95 92

  
96
    static Static4D rotateVectorByInvertedQuat(Static4D vector, Static4D quat)
93
    public static Static4D rotateVectorByQuat(Static4D vector, Static4D quat)
97 94
      {
98 95
      float qx = quat.get1();
99 96
      float qy = quat.get2();
......
101 98
      float qw = quat.get4();
102 99

  
103 100
      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
104
      Static4D tmp = quatMultiply(quatInverted,vector);
101
      Static4D tmp = quatMultiply(quat,vector);
105 102

  
106
      return quatMultiply(tmp,quat);
103
      return quatMultiply(tmp,quatInverted);
107 104
      }
108 105

  
109 106
///////////////////////////////////////////////////////////////////////////////////////////////////
110
// rotate 'vector' by quat  ( i.e. return quat*vector*(quat^-1) )
107
// rotate 'vector' by quat^(-1)  ( i.e. return (quat^-1)*vector*quat )
111 108

  
112
    static Static4D rotateVectorByQuat(Static4D vector, Static4D quat)
109
    public static Static4D rotateVectorByInvertedQuat(Static4D vector, Static4D quat)
113 110
      {
114 111
      float qx = quat.get1();
115 112
      float qy = quat.get2();
......
117 114
      float qw = quat.get4();
118 115

  
119 116
      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
120
      Static4D tmp = quatMultiply(quat,vector);
121

  
122
      return quatMultiply(tmp,quatInverted);
123
      }
124

  
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

  
127
    private int faceTouched(int xTouch, int yTouch)
128
      {
129
      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
130

  
131
      convertTouchPointToScreenSpace(xTouch,yTouch);
132
      convertCameraPointToScreenSpace();
133

  
134
      for(int face=FRONT; face<=BOTTOM; face++)
135
        {
136
        if( faceIsVisible(face,cubeHalfSize) )
137
          {
138
          castTouchPointOntoFace(face,cubeHalfSize, mTouchPointCastOntoFace);
139

  
140
          float qX= (mTouchPointCastOntoFace[0]+cubeHalfSize) / (2*cubeHalfSize);
141
          float qY= (mTouchPointCastOntoFace[1]+cubeHalfSize) / (2*cubeHalfSize);
142
          float qZ= (mTouchPointCastOntoFace[2]+cubeHalfSize) / (2*cubeHalfSize);
143

  
144
          if( qX<=1 && qX>=0 && qY<=1 && qY>=0 && qZ<=1 && qZ>=0 ) return face;
145
          }
146
        }
117
      Static4D tmp = quatMultiply(quatInverted,vector);
147 118

  
148
      return NONE;
119
      return quatMultiply(tmp,quat);
149 120
      }
150 121

  
151 122
///////////////////////////////////////////////////////////////////////////////////////////////////
152 123

  
153
    private void addNewRotation(int x, int y)
124
    void setScreenSize(int width, int height)
154 125
      {
155
      float cubeHalfSize= mRenderer.returnCubeSizeInScreenSpace()*0.5f;
156

  
157
      convertTouchPointToScreenSpace(x,y);
158
      castTouchPointOntoFace(mLastTouchedFace,cubeHalfSize,mDiff);
159

  
160
      mDiff[0] -= mTouchPointCastOntoFace[0];
161
      mDiff[1] -= mTouchPointCastOntoFace[1];
162
      mDiff[2] -= mTouchPointCastOntoFace[2];
163

  
164
      int xAxis = retFaceXaxis(mLastTouchedFace);
165
      int yAxis = retFaceYaxis(mLastTouchedFace);
166
      mRotationVect = (isVertical( mDiff[xAxis], mDiff[yAxis]) ? VECT[xAxis]:VECT[yAxis]);
167
      float offset= (mTouchPointCastOntoFace[mRotationVect]+cubeHalfSize)/(2*cubeHalfSize);
168

  
169
      mTouchPoint[0] = mPoint[0];
170
      mTouchPoint[1] = mPoint[1];
171
      mTouchPoint[2] = mPoint[2];
126
      mScreenWidth = width;
127
      mScreenHeight= height;
172 128

  
173
      RubikCube cube = mRenderer.getCube();
174
      cube.addNewRotation(mRotationVect, (int)(cube.getSize()*offset) );
129
      mScreenMin = width<height ? width:height;
175 130
      }
176 131

  
177 132
///////////////////////////////////////////////////////////////////////////////////////////////////
178 133

  
179
    private boolean isVertical(float x, float y)
134
    void setCameraDist(float distance)
180 135
      {
181
      return (y>x) ? (y>=-x) : (y< -x);
136
      mCameraDistance = distance;
182 137
      }
183 138

  
184 139
///////////////////////////////////////////////////////////////////////////////////////////////////
185 140

  
186
    private void continueRotation(int x, int y)
141
    RubikRenderer getRenderer()
187 142
      {
188
      convertTouchPointToScreenSpace(x,y);
189

  
190
      mDiff[0] = mPoint[0]-mTouchPoint[0];
191
      mDiff[1] = mPoint[1]-mTouchPoint[1];
192
      mDiff[2] = mPoint[2]-mTouchPoint[2];
193

  
194
      int xAxis= retFaceXaxis(mLastTouchedFace);
195
      int yAxis= retFaceYaxis(mLastTouchedFace);
196
      int sign = retFaceRotationSign(mLastTouchedFace);
197
      float angle = (mRotationVect==xAxis ? mDiff[yAxis] : -mDiff[xAxis]);
198

  
199
      mRenderer.getCube().continueRotation(SWIPING_SENSITIVITY*sign*angle/mScreenMin);
143
      return mRenderer;
200 144
      }
201 145

  
202 146
///////////////////////////////////////////////////////////////////////////////////////////////////
203 147

  
204
    private void finishRotation()
148
    int getRedButton()
205 149
      {
206
      mRenderer.finishRotation();
150
      return mButton;
207 151
      }
208 152

  
209 153
///////////////////////////////////////////////////////////////////////////////////////////////////
210 154

  
211
    private Static4D quatFromDrag(float dragX, float dragY)
155
    void markButton(int button)
212 156
      {
213
      float axisX = dragY;  // inverted X and Y - rotation axis is perpendicular to (dragX,dragY)
214
      float axisY = dragX;  // Why not (-dragY, dragX) ? because Y axis is also inverted!
215
      float axisZ = 0;
216
      float axisL = (float)Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
157
      mButton = button;
158
      RubikActivity act = (RubikActivity)getContext();
217 159

  
218
      if( axisL>0 )
160
      for(int b=0; b<RubikSize.LENGTH; b++)
219 161
        {
220
        axisX /= axisL;
221
        axisY /= axisL;
222
        axisZ /= axisL;
162
        Drawable d = act.findViewById(b).getBackground();
223 163

  
224
        float ratio = axisL/mScreenMin;
225
        ratio = ratio - (int)ratio;     // the cos() is only valid in (0,Pi)
226

  
227
        float cosA = (float)Math.cos(Math.PI*ratio);
228
        float sinA = (float)Math.sqrt(1-cosA*cosA);
229

  
230
        return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
164
        if( b==button )
165
          {
166
          d.setColorFilter(ContextCompat.getColor(act,R.color.red), PorterDuff.Mode.MULTIPLY);
167
          }
168
        else
169
          {
170
          d.clearColorFilter();
171
          }
231 172
        }
232

  
233
      return new Static4D(0f, 0f, 0f, 1f);
234 173
      }
235 174

  
236 175
///////////////////////////////////////////////////////////////////////////////////////////////////
237 176

  
238
    private boolean faceIsVisible(int face, float cubeHalfSize)
177
    void addSizeButtons(RubikActivity act)
239 178
      {
240
      int sign = retFaceSign(face);
241
      int zAxis= retFaceZaxis(face);
242

  
243
      return sign*mCamera[zAxis] > cubeHalfSize;
244
      }
245

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

  
248
    private void convertTouchPointToScreenSpace(int x, int y)
249
      {
250
      float halfScrWidth  = mScreenWidth *0.5f;
251
      float halfScrHeight = mScreenHeight*0.5f;
252
      Static4D touchPoint = new Static4D(x-halfScrWidth, halfScrHeight-y, 0, 0);
253
      Static4D rotatedTouchPoint= rotateVectorByInvertedQuat(touchPoint, mQuatAccumulated);
254

  
255
      mPoint[0] = rotatedTouchPoint.get1();
256
      mPoint[1] = rotatedTouchPoint.get2();
257
      mPoint[2] = rotatedTouchPoint.get3();
179
      LinearLayout layout = act.findViewById(R.id.sizeLayout);
180
      DisplayMetrics metrics = getResources().getDisplayMetrics();
181
      float scale = metrics.density;
182
      int size = (int)(64*scale +0.5f);
183
      int padding = (int)(3*scale + 0.5f);
184
      ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size,size);
185

  
186
      for(int i=0; i<RubikSize.LENGTH; i++)
187
        {
188
        ImageButton button = new ImageButton(act);
189
        button.setLayoutParams(params);
190
        button.setId(i);
191
        button.setPadding(padding,0,padding,0);
192
        int iconID = RubikSize.getSize(i).getIconID();
193
        button.setImageResource(iconID);
194
        button.setOnClickListener(act);
195
        layout.addView(button);
196
        }
258 197
      }
259 198

  
260 199
///////////////////////////////////////////////////////////////////////////////////////////////////
261 200

  
262
    private void convertCameraPointToScreenSpace()
201
    void enterScrambleMode()
263 202
      {
264
      Static4D cameraPoint = new Static4D(0, 0, mCameraDistance, 0);
265
      Static4D rotatedCamera= rotateVectorByInvertedQuat(cameraPoint, mQuatAccumulated);
266

  
267
      mCamera[0] = rotatedCamera.get1();
268
      mCamera[1] = rotatedCamera.get2();
269
      mCamera[2] = rotatedCamera.get3();
270
      }
203
      if( !mInScrambleMode )
204
        {
205
        mInScrambleMode = true;
271 206

  
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
274
// cast this touch point onto the surface defined by the 'face' and write the cast coords to 'output'.
275
// Center of the 'face' = (0,0), third coord always +- cubeHalfSize.
207
        android.util.Log.e("view", "entering scramble mode");
276 208

  
277
    private void castTouchPointOntoFace(int face, float cubeHalfSize, float[] output)
278
      {
279
      int sign = retFaceSign(face);
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff