Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / Cubit.java @ 7437ebb3

1 70b76549 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 b32444ee Leszek Koltunski
import org.distorted.library.mesh.MeshBase;
30 70b76549 Leszek Koltunski
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 b32444ee Leszek Koltunski
  private MeshBase mMesh;
47 70b76549 Leszek Koltunski
  private Static3D mRotationAxis;
48
  private MatrixEffectRotate mRotateEffect;
49 0e7bcfb1 Leszek Koltunski
  private Static3D mCurrentPosition;
50 e844c116 Leszek Koltunski
  private int mNumAxis;
51 70b76549 Leszek Koltunski
52
  Dynamic1D mRotationAngle;
53
  DistortedNode mNode;
54
  DistortedEffects mEffect;
55
  Static4D mQuatScramble;
56 66cbdd21 Leszek Koltunski
  float[] mRotationRow;
57 70b76549 Leszek Koltunski
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 9f4c44fe Leszek Koltunski
    final float MAX_ERROR = 0.0001f;
69
70 70b76549 Leszek Koltunski
    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 9f4c44fe Leszek Koltunski
      if( diff*diff<MAX_ERROR ) x = legal;
80 70b76549 Leszek Koltunski
      diff = y-legal;
81 9f4c44fe Leszek Koltunski
      if( diff*diff<MAX_ERROR ) y = legal;
82 70b76549 Leszek Koltunski
      diff = z-legal;
83 9f4c44fe Leszek Koltunski
      if( diff*diff<MAX_ERROR ) z = legal;
84 70b76549 Leszek Koltunski
      diff = w-legal;
85 9f4c44fe Leszek Koltunski
      if( diff*diff<MAX_ERROR ) w = legal;
86 70b76549 Leszek Koltunski
      }
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 49f67f9b Leszek Koltunski
    final int NEAREST = 360/mParent.getBasicAngle();
128 70b76549 Leszek Koltunski
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 94ad5a8f Leszek Koltunski
  private void modifyCurrentPosition(Static4D quat)
138 70b76549 Leszek Koltunski
    {
139 94ad5a8f Leszek Koltunski
    float cubitCenterX = mCurrentPosition.get0();
140
    float cubitCenterY = mCurrentPosition.get1();
141
    float cubitCenterZ = mCurrentPosition.get2();
142 70b76549 Leszek Koltunski
143
    Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
144
    Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
145
146 49f67f9b Leszek Koltunski
    float rotatedX = rotatedCenter.get0();
147
    float rotatedY = rotatedCenter.get1();
148
    float rotatedZ = rotatedCenter.get2();
149 70b76549 Leszek Koltunski
150 94ad5a8f Leszek Koltunski
    mCurrentPosition.set(rotatedX, rotatedY, rotatedZ);
151
    mParent.clampPos(mCurrentPosition);
152 70b76549 Leszek Koltunski
153 10a2e360 Leszek Koltunski
    computeRotationRow();
154
    }
155
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157 e844c116 Leszek Koltunski
// cast current position on axis; use mStart and mStep to compute the rotation row for each axis.
158 10a2e360 Leszek Koltunski
159
  private void computeRotationRow()
160
    {
161 e844c116 Leszek Koltunski
    float tmp;
162
    Static3D axis;
163
    float x = mCurrentPosition.get0();
164
    float y = mCurrentPosition.get1();
165
    float z = mCurrentPosition.get2();
166
167
    for(int i=0; i<mNumAxis; i++)
168
      {
169
      axis = mParent.ROTATION_AXIS[i];
170
      tmp = x*axis.get0() + y*axis.get1() + z*axis.get2();
171 66cbdd21 Leszek Koltunski
      mRotationRow[i] = (tmp-mParent.mStart)/mParent.mStep;
172 e844c116 Leszek Koltunski
      }
173 70b76549 Leszek Koltunski
    }
174
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176
177 b32444ee Leszek Koltunski
  Cubit(RubikObject parent, MeshBase mesh, Static3D position)
178 70b76549 Leszek Koltunski
    {
179
    float x = position.get0();
180
    float y = position.get1();
181
    float z = position.get2();
182
183 49f67f9b Leszek Koltunski
    Static3D vector = new Static3D(x,y,z);
184 70b76549 Leszek Koltunski
185
    mParent          = parent;
186 b32444ee Leszek Koltunski
    mMesh            = mesh;
187 70b76549 Leszek Koltunski
    mOrigPosition    = new Static3D(x,y,z);
188
    mQuatScramble    = new Static4D(0,0,0,1);
189
    mRotationAngle   = new Dynamic1D();
190
    mRotationAxis    = new Static3D(1,0,0);
191
    mCurrentPosition = position;
192
    mRotateEffect    = new MatrixEffectRotate(mRotationAngle, mRotationAxis, matrCenter);
193
194 e844c116 Leszek Koltunski
    mNumAxis     = mParent.ROTATION_AXIS.length;
195 66cbdd21 Leszek Koltunski
    mRotationRow = new float[mNumAxis];
196 10a2e360 Leszek Koltunski
    computeRotationRow();
197
198 36273130 Leszek Koltunski
    mEffect = new DistortedEffects();
199 70b76549 Leszek Koltunski
    mEffect.apply(mParent.mSinkEffect);
200
    mEffect.apply( new MatrixEffectMove(vector) );
201
    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, matrCenter));
202
    mEffect.apply(mRotateEffect);
203
    mEffect.apply(mParent.mQuatAEffect);
204
    mEffect.apply(mParent.mQuatCEffect);
205
    mEffect.apply(mParent.mScaleEffect);
206
207 b32444ee Leszek Koltunski
    mNode = new DistortedNode(mParent.mTexture,mEffect,mMesh);
208 70b76549 Leszek Koltunski
    }
209
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211
212
  void savePreferences(SharedPreferences.Editor editor)
213
    {
214
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
215
216
    editor.putFloat("qx_"+number, mQuatScramble.get0());
217
    editor.putFloat("qy_"+number, mQuatScramble.get1());
218
    editor.putFloat("qz_"+number, mQuatScramble.get2());
219
    editor.putFloat("qw_"+number, mQuatScramble.get3());
220
    }
221
222
///////////////////////////////////////////////////////////////////////////////////////////////////
223
224
  void restorePreferences(SharedPreferences preferences)
225
    {
226
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
227
228
    float qx = preferences.getFloat("qx_"+number, 0.0f);
229
    float qy = preferences.getFloat("qy_"+number, 0.0f);
230
    float qz = preferences.getFloat("qz_"+number, 0.0f);
231
    float qw = preferences.getFloat("qw_"+number, 1.0f);
232
233
    mQuatScramble.set(qx,qy,qz,qw);
234 94ad5a8f Leszek Koltunski
    modifyCurrentPosition(mQuatScramble);
235
    }
236
237
///////////////////////////////////////////////////////////////////////////////////////////////////
238
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
239
// then if it were rotated by quaternion 'quat'.
240
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
241
// middle squares get interchanged. No visible difference!
242
//
243
// So: this is true iff the cubit
244
// a) is a corner or edge and the quaternions are the same
245
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
246
247
  boolean thereIsNoVisibleDifference(Static4D quat)
248
    {
249
    if ( mQuatScramble.get0()==quat.get0() &&
250
         mQuatScramble.get1()==quat.get1() &&
251
         mQuatScramble.get2()==quat.get2() &&
252
         mQuatScramble.get3()==quat.get3()  ) return true;
253
254
    int belongsToHowManyFaces = 0;
255
    int size = mParent.getSize()-1;
256 7437ebb3 Leszek Koltunski
    float row;
257
    final float MAX_ERROR = 0.01f;
258 94ad5a8f Leszek Koltunski
259
    for(int i=0; i<mNumAxis; i++)
260
      {
261 7437ebb3 Leszek Koltunski
      row = mRotationRow[i];
262
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
263
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
264 94ad5a8f Leszek Koltunski
      }
265
266
    switch(belongsToHowManyFaces)
267
      {
268
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
269
      case 1 :                // cubit that lies inside one of the faces
270
               float cubitCenterX = mCurrentPosition.get0();
271
               float cubitCenterY = mCurrentPosition.get1();
272
               float cubitCenterZ = mCurrentPosition.get2();
273
274
               Static4D cubitCenter = new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
275
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
276
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, mQuatScramble );
277
278 7437ebb3 Leszek Koltunski
               float row1, row2, row3, row4;
279 94ad5a8f Leszek Koltunski
               float ax,ay,az;
280
               Static3D axis;
281
               float x1 = rotated1.get0();
282
               float y1 = rotated1.get1();
283
               float z1 = rotated1.get2();
284
               float x2 = rotated2.get0();
285
               float y2 = rotated2.get1();
286
               float z2 = rotated2.get2();
287
288
               for(int i=0; i<mNumAxis; i++)
289
                 {
290
                 axis = mParent.ROTATION_AXIS[i];
291
                 ax = axis.get0();
292
                 ay = axis.get1();
293
                 az = axis.get2();
294
295 7437ebb3 Leszek Koltunski
                 row1 = ((x1*ax + y1*ay + z1*az) - mParent.mStart) / mParent.mStep;
296
                 row2 = ((x2*ax + y2*ay + z2*az) - mParent.mStart) / mParent.mStep;
297
                 row3 = row1 - size;
298
                 row4 = row2 - size;
299 94ad5a8f Leszek Koltunski
300 7437ebb3 Leszek Koltunski
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
301
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
302 94ad5a8f Leszek Koltunski
                   {
303
                   return true;
304
                   }
305
                 }
306
               return false;
307
      default: return false;  // edge or corner
308
309
310
      }
311 70b76549 Leszek Koltunski
    }
312
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314
315
  long finishRotationNow(EffectListener listener)
316
    {
317
    int pointNum = mRotationAngle.getNumPoints();
318
319
    if( pointNum>=1 )
320
      {
321
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
322
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
323
      mParent.mRotationAngleStatic.set0(startingAngle);
324
      mParent.mRotationAngleFinal.set0(nearestAngleInDegrees);
325
      mParent.mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
326
      return setUpCallback(listener);
327
      }
328
329
    return 0;
330
    }
331
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333
334 efef689c Leszek Koltunski
  Static4D returnRotationQuat(int axis)
335 70b76549 Leszek Koltunski
    {
336
    int pointNum = mRotationAngle.getNumPoints();
337
338
    if( pointNum>=1 )
339
      {
340 efef689c Leszek Koltunski
      float axisX = mParent.ROTATION_AXIS[axis].get0();
341
      float axisY = mParent.ROTATION_AXIS[axis].get1();
342
      float axisZ = mParent.ROTATION_AXIS[axis].get2();
343
344 70b76549 Leszek Koltunski
      float startingAngle = mRotationAngle.getPoint(pointNum-1).get0();
345
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
346
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
347
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
348
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
349 efef689c Leszek Koltunski
      return new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
350 70b76549 Leszek Koltunski
      }
351
352
    return null;
353
    }
354
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356
357
  void removeRotationNow(Static4D quat)
358
    {
359
    mRotationAngle.removeAll();
360
    mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
361
    normalizeScrambleQuat( mQuatScramble );
362 94ad5a8f Leszek Koltunski
    modifyCurrentPosition(quat);
363 70b76549 Leszek Koltunski
    }
364
365
///////////////////////////////////////////////////////////////////////////////////////////////////
366
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
367
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
368
// memory leak.
369
370
  void releaseResources()
371
    {
372 b32444ee Leszek Koltunski
    mMesh.markForDeletion();
373 70b76549 Leszek Koltunski
    mNode.markForDeletion();
374
    }
375
376
///////////////////////////////////////////////////////////////////////////////////////////////////
377
378
  void solve()
379
    {
380
    mQuatScramble.set(0,0,0,1);
381
    mCurrentPosition.set(mOrigPosition);
382 0e7bcfb1 Leszek Koltunski
    computeRotationRow();
383 70b76549 Leszek Koltunski
    }
384
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
387 efef689c Leszek Koltunski
  void beginNewRotation(int axis)
388 70b76549 Leszek Koltunski
    {
389 efef689c Leszek Koltunski
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
390 70b76549 Leszek Koltunski
    mRotationAngle.add(mParent.mRotationAngleStatic);
391
    }
392
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394
395 efef689c Leszek Koltunski
  void addNewRotation(int axis, long durationMillis, int angle)
396 70b76549 Leszek Koltunski
    {
397 efef689c Leszek Koltunski
    mRotationAxis.set( mParent.ROTATION_AXIS[axis] );
398 70b76549 Leszek Koltunski
    mRotationAngle.setDuration(durationMillis);
399
    mRotationAngle.resetToBeginning();
400
    mRotationAngle.add(new Static1D(0));
401
    mRotationAngle.add(new Static1D(angle));
402
    }
403
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405
406
  long setUpCallback(EffectListener listener)
407
    {
408
    mRotateEffect.notifyWhenFinished(listener);
409
    return mRotateEffect.getID();
410
    }
411
}