Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / Cubit.java @ 66cbdd21

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
  private int mNumAxis;
51

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

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

    
66
  private void normalizeScrambleQuat(Static4D quat)
67
    {
68
    final float MAX_ERROR = 0.0001f;
69

    
70
    float x = quat.get0();
71
    float y = quat.get1();
72
    float z = quat.get2();
73
    float w = quat.get3();
74
    float diff;
75

    
76
    for(float legal: mParent.LEGAL_QUATS)
77
      {
78
      diff = x-legal;
79
      if( diff*diff<MAX_ERROR ) x = legal;
80
      diff = y-legal;
81
      if( diff*diff<MAX_ERROR ) y = legal;
82
      diff = z-legal;
83
      if( diff*diff<MAX_ERROR ) z = legal;
84
      diff = w-legal;
85
      if( diff*diff<MAX_ERROR ) w = legal;
86
      }
87

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

    
120
    quat.set(x,y,z,w);
121
    }
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

    
125
  private int computeNearestAngle(float angle)
126
    {
127
    final int NEAREST = 360/mParent.getBasicAngle();
128

    
129
    int tmp = (int)((angle+NEAREST/2)/NEAREST);
130
    if( angle< -(NEAREST*0.5) ) tmp-=1;
131

    
132
    return NEAREST*tmp;
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

    
137
  private void modifyCurrentPosition(Static3D currentPosition, Static4D quat)
138
    {
139
    float cubitCenterX = currentPosition.get0();
140
    float cubitCenterY = currentPosition.get1();
141
    float cubitCenterZ = currentPosition.get2();
142

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

    
146
    float rotatedX = rotatedCenter.get0();
147
    float rotatedY = rotatedCenter.get1();
148
    float rotatedZ = rotatedCenter.get2();
149

    
150
    currentPosition.set(rotatedX, rotatedY, rotatedZ);
151
    mParent.clampPos(currentPosition);
152

    
153
    computeRotationRow();
154
    }
155

    
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158
// cast current position on axis; use mStart and mStep to compute the rotation row for each axis.
159

    
160
  private void computeRotationRow()
161
    {
162
    float tmp;
163
    Static3D axis;
164
    float x = mCurrentPosition.get0();
165
    float y = mCurrentPosition.get1();
166
    float z = mCurrentPosition.get2();
167

    
168
    for(int i=0; i<mNumAxis; i++)
169
      {
170
      axis = mParent.ROTATION_AXIS[i];
171
      tmp = x*axis.get0() + y*axis.get1() + z*axis.get2();
172
      mRotationRow[i] = (tmp-mParent.mStart)/mParent.mStep;
173
      }
174
    }
175

    
176
///////////////////////////////////////////////////////////////////////////////////////////////////
177

    
178
  Cubit(RubikObject parent, MeshBase mesh, Static3D position)
179
    {
180
    float x = position.get0();
181
    float y = position.get1();
182
    float z = position.get2();
183

    
184
    Static3D vector = new Static3D(x,y,z);
185

    
186
    mParent          = parent;
187
    mMesh            = mesh;
188
    mOrigPosition    = new Static3D(x,y,z);
189
    mQuatScramble    = new Static4D(0,0,0,1);
190
    mRotationAngle   = new Dynamic1D();
191
    mRotationAxis    = new Static3D(1,0,0);
192
    mCurrentPosition = position;
193
    mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, matrCenter);
194

    
195
    mNumAxis     = mParent.ROTATION_AXIS.length;
196
    mRotationRow = new float[mNumAxis];
197
    computeRotationRow();
198

    
199
    mEffect = new DistortedEffects();
200
    mEffect.apply(mParent.mSinkEffect);
201
    mEffect.apply( new MatrixEffectMove(vector) );
202
    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, matrCenter));
203
    mEffect.apply(mRotateEffect);
204
    mEffect.apply(mParent.mQuatAEffect);
205
    mEffect.apply(mParent.mQuatCEffect);
206
    mEffect.apply(mParent.mScaleEffect);
207

    
208
    mNode = new DistortedNode(mParent.mTexture,mEffect,mMesh);
209
    }
210

    
211
///////////////////////////////////////////////////////////////////////////////////////////////////
212

    
213
  void savePreferences(SharedPreferences.Editor editor)
214
    {
215
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
216

    
217
    editor.putFloat("qx_"+number, mQuatScramble.get0());
218
    editor.putFloat("qy_"+number, mQuatScramble.get1());
219
    editor.putFloat("qz_"+number, mQuatScramble.get2());
220
    editor.putFloat("qw_"+number, mQuatScramble.get3());
221
    }
222

    
223
///////////////////////////////////////////////////////////////////////////////////////////////////
224

    
225
  void restorePreferences(SharedPreferences preferences)
226
    {
227
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
228

    
229
    float qx = preferences.getFloat("qx_"+number, 0.0f);
230
    float qy = preferences.getFloat("qy_"+number, 0.0f);
231
    float qz = preferences.getFloat("qz_"+number, 0.0f);
232
    float qw = preferences.getFloat("qw_"+number, 1.0f);
233

    
234
    mQuatScramble.set(qx,qy,qz,qw);
235
    modifyCurrentPosition( mCurrentPosition, mQuatScramble);
236
    }
237

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

    
240
  long finishRotationNow(EffectListener listener)
241
    {
242
    int pointNum = mRotationAngle.getNumPoints();
243

    
244
    if( pointNum>=1 )
245
      {
246
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
247
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
248
      mParent.mRotationAngleStatic.set0(startingAngle);
249
      mParent.mRotationAngleFinal.set0(nearestAngleInDegrees);
250
      mParent.mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
251
      return setUpCallback(listener);
252
      }
253

    
254
    return 0;
255
    }
256

    
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258

    
259
  Static4D returnRotationQuat(int axis)
260
    {
261
    int pointNum = mRotationAngle.getNumPoints();
262

    
263
    if( pointNum>=1 )
264
      {
265
      float axisX = mParent.ROTATION_AXIS[axis].get0();
266
      float axisY = mParent.ROTATION_AXIS[axis].get1();
267
      float axisZ = mParent.ROTATION_AXIS[axis].get2();
268

    
269
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
270
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
271
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
272
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
273
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
274
      return new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
275
      }
276

    
277
    return null;
278
    }
279

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

    
282
  void removeRotationNow(Static4D quat)
283
    {
284
    mRotationAngle.removeAll();
285
    mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
286
    normalizeScrambleQuat( mQuatScramble );
287
    modifyCurrentPosition( mCurrentPosition,quat);
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
292
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
293
// memory leak.
294

    
295
  void releaseResources()
296
    {
297
    mMesh.markForDeletion();
298
    mNode.markForDeletion();
299
    }
300

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

    
303
  void solve()
304
    {
305
    mQuatScramble.set(0,0,0,1);
306
    mCurrentPosition.set(mOrigPosition);
307
    computeRotationRow();
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

    
312
  void beginNewRotation(int axis)
313
    {
314
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
315
    mRotationAngle.add(mParent.mRotationAngleStatic);
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  void addNewRotation(int axis, long durationMillis, int angle)
321
    {
322
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
323
    mRotationAngle.setDuration(durationMillis);
324
    mRotationAngle.resetToBeginning();
325
    mRotationAngle.add(new Static1D(0));
326
    mRotationAngle.add(new Static1D(angle));
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

    
331
  long setUpCallback(EffectListener listener)
332
    {
333
    mRotateEffect.notifyWhenFinished(listener);
334
    return mRotateEffect.getID();
335
    }
336
}
(1-1/8)