Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / Cubit.java @ 0e7bcfb1

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.object;
21

    
22
import android.content.SharedPreferences;
23

    
24
import org.distorted.library.effect.MatrixEffectMove;
25
import org.distorted.library.effect.MatrixEffectQuaternion;
26
import org.distorted.library.effect.MatrixEffectRotate;
27
import org.distorted.library.main.DistortedEffects;
28
import org.distorted.library.main.DistortedNode;
29
import org.distorted.library.mesh.MeshBase;
30
import org.distorted.library.message.EffectListener;
31
import org.distorted.library.type.Dynamic1D;
32
import org.distorted.library.type.Static1D;
33
import org.distorted.library.type.Static3D;
34
import org.distorted.library.type.Static4D;
35
import org.distorted.magic.RubikSurfaceView;
36

    
37
///////////////////////////////////////////////////////////////////////////////////////////////////
38

    
39
class Cubit
40
  {
41
  private static final Static3D matrCenter = new Static3D(0,0,0);
42

    
43
  private final Static3D mOrigPosition;
44

    
45
  private RubikObject mParent;
46
  private MeshBase mMesh;
47
  private Static3D mRotationAxis;
48
  private MatrixEffectRotate mRotateEffect;
49
  private Static3D mCurrentPosition;
50

    
51
  Dynamic1D mRotationAngle;
52
  DistortedNode mNode;
53
  DistortedEffects mEffect;
54
  Static4D mQuatScramble;
55
  int[] mRotationRow;
56

    
57
///////////////////////////////////////////////////////////////////////////////////////////////////
58
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
59
// correct the value of the 'scramble' quat to what it should be - one of the legal quats from the
60
// list LEGAL_QUATS.
61
//
62
// We also have to remember that the group of unit quaternions is a double-cover of rotations
63
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
64

    
65
  private void normalizeScrambleQuat(Static4D quat)
66
    {
67
    float x = quat.get0();
68
    float y = quat.get1();
69
    float z = quat.get2();
70
    float w = quat.get3();
71
    float diff;
72

    
73
    for(float legal: mParent.LEGAL_QUATS)
74
      {
75
      diff = x-legal;
76
      if( diff*diff<0.01f ) x = legal;
77
      diff = y-legal;
78
      if( diff*diff<0.01f ) y = legal;
79
      diff = z-legal;
80
      if( diff*diff<0.01f ) z = legal;
81
      diff = w-legal;
82
      if( diff*diff<0.01f ) w = legal;
83
      }
84

    
85
    if( w<0 )
86
      {
87
      w = -w;
88
      z = -z;
89
      y = -y;
90
      x = -x;
91
      }
92
    else if( w==0 )
93
      {
94
      if( z<0 )
95
        {
96
        z = -z;
97
        y = -y;
98
        x = -x;
99
        }
100
      else if( z==0 )
101
        {
102
        if( y<0 )
103
          {
104
          y = -y;
105
          x = -x;
106
          }
107
        else if( y==0 )
108
          {
109
          if( x<0 )
110
            {
111
            x = -x;
112
            }
113
          }
114
        }
115
      }
116

    
117
    quat.set(x,y,z,w);
118
    }
119

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121

    
122
  private int computeNearestAngle(float angle)
123
    {
124
    final int NEAREST = 90;
125

    
126
    int tmp = (int)((angle+NEAREST/2)/NEAREST);
127
    if( angle< -(NEAREST*0.5) ) tmp-=1;
128

    
129
    return NEAREST*tmp;
130
    }
131

    
132
///////////////////////////////////////////////////////////////////////////////////////////////////
133

    
134
  private void modifyCurrentPosition(Static3D currentPosition, Static4D quat)
135
    {
136
    float diff = 0.5f*(mParent.mSize-1);
137
    float cubitCenterX = currentPosition.get0() - diff;
138
    float cubitCenterY = currentPosition.get1() - diff;
139
    float cubitCenterZ = currentPosition.get2() - diff;
140

    
141
    Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
142
    Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
143

    
144
    float rotatedX = rotatedCenter.get0() + diff;
145
    float rotatedY = rotatedCenter.get1() + diff;
146
    float rotatedZ = rotatedCenter.get2() + diff;
147

    
148
    int roundedX = (int)(rotatedX+0.1f);
149
    int roundedY = (int)(rotatedY+0.1f);
150
    int roundedZ = (int)(rotatedZ+0.1f);
151

    
152
    currentPosition.set(roundedX, roundedY, roundedZ);
153
    computeRotationRow();
154
    }
155

    
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158
// TODO: this is only right in case of RubikCube
159

    
160
  private void computeRotationRow()
161
    {
162
    mRotationRow[0] = (int)mCurrentPosition.get0();
163
    mRotationRow[1] = (int)mCurrentPosition.get1();
164
    mRotationRow[2] = (int)mCurrentPosition.get2();
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

    
169
  Cubit(RubikObject parent, MeshBase mesh, Static3D position)
170
    {
171
    float x = position.get0();
172
    float y = position.get1();
173
    float z = position.get2();
174

    
175
    float nc = parent.mSize*0.5f;
176
    Static3D vector = new Static3D(x-nc+0.5f, y-nc+0.5f, z-nc+0.5f);
177

    
178
    mParent          = parent;
179
    mMesh            = mesh;
180
    mOrigPosition    = new Static3D(x,y,z);
181
    mQuatScramble    = new Static4D(0,0,0,1);
182
    mRotationAngle   = new Dynamic1D();
183
    mRotationAxis    = new Static3D(1,0,0);
184
    mCurrentPosition = position;
185
    mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, matrCenter);
186

    
187
    mRotationRow = new int[mParent.ROTATION_AXIS.length];
188
    computeRotationRow();
189

    
190
    mEffect = new DistortedEffects();
191
    mEffect.apply(mParent.mSinkEffect);
192
    mEffect.apply( new MatrixEffectMove(vector) );
193
    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, matrCenter));
194
    mEffect.apply(mRotateEffect);
195
    mEffect.apply(mParent.mQuatAEffect);
196
    mEffect.apply(mParent.mQuatCEffect);
197
    mEffect.apply(mParent.mScaleEffect);
198

    
199
    mNode = new DistortedNode(mParent.mTexture,mEffect,mMesh);
200
    }
201

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

    
204
  void savePreferences(SharedPreferences.Editor editor)
205
    {
206
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
207

    
208
    editor.putFloat("qx_"+number, mQuatScramble.get0());
209
    editor.putFloat("qy_"+number, mQuatScramble.get1());
210
    editor.putFloat("qz_"+number, mQuatScramble.get2());
211
    editor.putFloat("qw_"+number, mQuatScramble.get3());
212
    }
213

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

    
216
  void restorePreferences(SharedPreferences preferences)
217
    {
218
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
219

    
220
    float qx = preferences.getFloat("qx_"+number, 0.0f);
221
    float qy = preferences.getFloat("qy_"+number, 0.0f);
222
    float qz = preferences.getFloat("qz_"+number, 0.0f);
223
    float qw = preferences.getFloat("qw_"+number, 1.0f);
224

    
225
    mQuatScramble.set(qx,qy,qz,qw);
226
    modifyCurrentPosition( mCurrentPosition, mQuatScramble);
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230

    
231
  long finishRotationNow(EffectListener listener)
232
    {
233
    int pointNum = mRotationAngle.getNumPoints();
234

    
235
    if( pointNum>=1 )
236
      {
237
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
238
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
239
      mParent.mRotationAngleStatic.set0(startingAngle);
240
      mParent.mRotationAngleFinal.set0(nearestAngleInDegrees);
241
      mParent.mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
242
      return setUpCallback(listener);
243
      }
244

    
245
    return 0;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249

    
250
  Static4D returnRotationQuat(int axis)
251
    {
252
    int pointNum = mRotationAngle.getNumPoints();
253

    
254
    if( pointNum>=1 )
255
      {
256
      float axisX = mParent.ROTATION_AXIS[axis].get0();
257
      float axisY = mParent.ROTATION_AXIS[axis].get1();
258
      float axisZ = mParent.ROTATION_AXIS[axis].get2();
259

    
260
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
261
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
262
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
263
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
264
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
265
      return new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
266
      }
267

    
268
    return null;
269
    }
270

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

    
273
  void removeRotationNow(Static4D quat)
274
    {
275
    mRotationAngle.removeAll();
276
    mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
277
    normalizeScrambleQuat( mQuatScramble );
278
    modifyCurrentPosition( mCurrentPosition,quat);
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
283
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
284
// memory leak.
285

    
286
  void releaseResources()
287
    {
288
    mMesh.markForDeletion();
289
    mNode.markForDeletion();
290
    }
291

    
292
///////////////////////////////////////////////////////////////////////////////////////////////////
293

    
294
  void solve()
295
    {
296
    mQuatScramble.set(0,0,0,1);
297
    mCurrentPosition.set(mOrigPosition);
298
    computeRotationRow();
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  void beginNewRotation(int axis)
304
    {
305
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
306
    mRotationAngle.add(mParent.mRotationAngleStatic);
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
  void addNewRotation(int axis, long durationMillis, int angle)
312
    {
313
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
314
    mRotationAngle.setDuration(durationMillis);
315
    mRotationAngle.resetToBeginning();
316
    mRotationAngle.add(new Static1D(0));
317
    mRotationAngle.add(new Static1D(angle));
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

    
322
  long setUpCallback(EffectListener listener)
323
    {
324
    mRotateEffect.notifyWhenFinished(listener);
325
    return mRotateEffect.getID();
326
    }
327
}
(1-1/6)