Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / Cubit.java @ 27e6c301

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.objects;
21

    
22
import android.content.SharedPreferences;
23

    
24
import org.distorted.library.effect.MatrixEffectQuaternion;
25
import org.distorted.library.main.DistortedEffects;
26
import org.distorted.library.type.Static3D;
27
import org.distorted.library.type.Static4D;
28
import org.distorted.main.RubikSurfaceView;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31

    
32
class Cubit
33
  {
34
  private static final Static3D MATRIX_CENTER = new Static3D(0,0,0);
35

    
36
  private final Static3D mOrigPosition;
37

    
38
  private RubikObject mParent;
39
  private Static3D mCurrentPosition;
40
  private int mNumAxis;
41

    
42
  DistortedEffects mEffect;
43
  Static4D mQuatScramble;
44
  float[] mRotationRow;
45

    
46
///////////////////////////////////////////////////////////////////////////////////////////////////
47

    
48
  Cubit(RubikObject parent, Static3D position)
49
    {
50
    float x = position.get0();
51
    float y = position.get1();
52
    float z = position.get2();
53

    
54
    mParent          = parent;
55
    mOrigPosition    = new Static3D(x,y,z);
56
    mQuatScramble    = new Static4D(0,0,0,1);
57

    
58
    mCurrentPosition = position;
59

    
60
    mNumAxis     = mParent.ROTATION_AXIS.length;
61
    mRotationRow = new float[mNumAxis];
62
    computeRotationRow();
63

    
64
    mEffect = new DistortedEffects();
65
    mEffect.apply( new MatrixEffectQuaternion(mQuatScramble, MATRIX_CENTER));
66
    }
67

    
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
70
// correct the value of the 'scramble' quat to what it should be - one of the legal quats from the
71
// list LEGAL_QUATS.
72
//
73
// We also have to remember that the group of unit quaternions is a double-cover of rotations
74
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
75

    
76
  private void normalizeScrambleQuat(Static4D quat)
77
    {
78
    final float MAX_ERROR = 0.0001f;
79

    
80
    float x = quat.get0();
81
    float y = quat.get1();
82
    float z = quat.get2();
83
    float w = quat.get3();
84
    float diff;
85

    
86
    for(float legal: mParent.LEGAL_QUATS)
87
      {
88
      diff = x-legal;
89
      if( diff*diff<MAX_ERROR ) x = legal;
90
      diff = y-legal;
91
      if( diff*diff<MAX_ERROR ) y = legal;
92
      diff = z-legal;
93
      if( diff*diff<MAX_ERROR ) z = legal;
94
      diff = w-legal;
95
      if( diff*diff<MAX_ERROR ) w = legal;
96
      }
97

    
98
    if( w<0 )
99
      {
100
      w = -w;
101
      z = -z;
102
      y = -y;
103
      x = -x;
104
      }
105
    else if( w==0 )
106
      {
107
      if( z<0 )
108
        {
109
        z = -z;
110
        y = -y;
111
        x = -x;
112
        }
113
      else if( z==0 )
114
        {
115
        if( y<0 )
116
          {
117
          y = -y;
118
          x = -x;
119
          }
120
        else if( y==0 )
121
          {
122
          if( x<0 )
123
            {
124
            x = -x;
125
            }
126
          }
127
        }
128
      }
129

    
130
    quat.set(x,y,z,w);
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

    
135
  private void modifyCurrentPosition(Static4D quat)
136
    {
137
    float cubitCenterX = mCurrentPosition.get0();
138
    float cubitCenterY = mCurrentPosition.get1();
139
    float cubitCenterZ = mCurrentPosition.get2();
140

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

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

    
148
    mCurrentPosition.set(rotatedX, rotatedY, rotatedZ);
149
    mParent.clampPos(mCurrentPosition);
150

    
151
    computeRotationRow();
152
    }
153

    
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155
// cast current position on axis; use mStart and mStep to compute the rotation row for each axis.
156

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

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

    
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174

    
175
  int computeAssociation()
176
    {
177
    int row, result = 0, accumulativeShift = 0;
178

    
179
    for(int axis=0; axis<mNumAxis; axis++)
180
      {
181
      row = (int)(mRotationRow[axis]+0.5f);
182
      result += (1<<(row+accumulativeShift));
183
      accumulativeShift += RubikObjectList.MAX_SIZE;
184
      }
185

    
186
    return result;
187
    }
188

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

    
191
  void savePreferences(SharedPreferences.Editor editor)
192
    {
193
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
194

    
195
    editor.putFloat("qx_"+number, mQuatScramble.get0());
196
    editor.putFloat("qy_"+number, mQuatScramble.get1());
197
    editor.putFloat("qz_"+number, mQuatScramble.get2());
198
    editor.putFloat("qw_"+number, mQuatScramble.get3());
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

    
203
  void restorePreferences(SharedPreferences preferences)
204
    {
205
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
206

    
207
    float qx = preferences.getFloat("qx_"+number, 0.0f);
208
    float qy = preferences.getFloat("qy_"+number, 0.0f);
209
    float qz = preferences.getFloat("qz_"+number, 0.0f);
210
    float qw = preferences.getFloat("qw_"+number, 1.0f);
211

    
212
    mQuatScramble.set(qx,qy,qz,qw);
213
    modifyCurrentPosition(mQuatScramble);
214
    }
215

    
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
218
// then if it were rotated by quaternion 'quat'.
219
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
220
// middle squares get interchanged. No visible difference!
221
//
222
// So: this is true iff the cubit
223
// a) is a corner or edge and the quaternions are the same
224
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
225

    
226
  boolean thereIsNoVisibleDifference(Static4D quat)
227
    {
228
    if ( mQuatScramble.get0()==quat.get0() &&
229
         mQuatScramble.get1()==quat.get1() &&
230
         mQuatScramble.get2()==quat.get2() &&
231
         mQuatScramble.get3()==quat.get3()  ) return true;
232

    
233
    int belongsToHowManyFaces = 0;
234
    int size = mParent.getSize()-1;
235
    float row;
236
    final float MAX_ERROR = 0.01f;
237

    
238
    for(int i=0; i<mNumAxis; i++)
239
      {
240
      row = mRotationRow[i];
241
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
242
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
243
      }
244

    
245
    switch(belongsToHowManyFaces)
246
      {
247
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
248
      case 1 :                // cubit that lies inside one of the faces
249
               float cubitCenterX = mCurrentPosition.get0();
250
               float cubitCenterY = mCurrentPosition.get1();
251
               float cubitCenterZ = mCurrentPosition.get2();
252

    
253
               Static4D cubitCenter = new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
254
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
255
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, mQuatScramble );
256

    
257
               float row1, row2, row3, row4;
258
               float ax,ay,az;
259
               Static3D axis;
260
               float x1 = rotated1.get0();
261
               float y1 = rotated1.get1();
262
               float z1 = rotated1.get2();
263
               float x2 = rotated2.get0();
264
               float y2 = rotated2.get1();
265
               float z2 = rotated2.get2();
266

    
267
               for(int i=0; i<mNumAxis; i++)
268
                 {
269
                 axis = mParent.ROTATION_AXIS[i];
270
                 ax = axis.get0();
271
                 ay = axis.get1();
272
                 az = axis.get2();
273

    
274
                 row1 = ((x1*ax + y1*ay + z1*az) - mParent.mStart) / mParent.mStep;
275
                 row2 = ((x2*ax + y2*ay + z2*az) - mParent.mStart) / mParent.mStep;
276
                 row3 = row1 - size;
277
                 row4 = row2 - size;
278

    
279
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
280
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
281
                   {
282
                   return true;
283
                   }
284
                 }
285
               return false;
286
      default: return false;  // edge or corner
287
      }
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291

    
292
  void removeRotationNow(Static4D quat)
293
    {
294
    mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
295
    normalizeScrambleQuat( mQuatScramble );
296
    modifyCurrentPosition(quat);
297
    }
298

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

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

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309

    
310
  float getDistSquared(float[] point)
311
    {
312
    float dx = mCurrentPosition.get0() - point[0];
313
    float dy = mCurrentPosition.get1() - point[1];
314
    float dz = mCurrentPosition.get2() - point[2];
315

    
316
    return dx*dx + dy*dy + dz*dz;
317
    }
318
}
(1-1/8)