Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / Cubit.java @ 10585385

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.type.Static3D;
25
import org.distorted.library.type.Static4D;
26
import org.distorted.main.RubikSurfaceView;
27

    
28
///////////////////////////////////////////////////////////////////////////////////////////////////
29

    
30
class Cubit
31
  {
32
  private final Static3D mOrigPosition;
33

    
34
  private RubikObject mParent;
35
  private Static3D mCurrentPosition;
36
  private int mNumAxis;
37

    
38
  Static4D mQuatScramble;
39
  float[] mRotationRow;
40

    
41
///////////////////////////////////////////////////////////////////////////////////////////////////
42

    
43
  Cubit(RubikObject parent, Static3D position)
44
    {
45
    float x = position.get0();
46
    float y = position.get1();
47
    float z = position.get2();
48

    
49
    mParent          = parent;
50
    mOrigPosition    = new Static3D(x,y,z);
51
    mQuatScramble    = new Static4D(0,0,0,1);
52

    
53
    mCurrentPosition = position;
54

    
55
    mNumAxis     = mParent.ROTATION_AXIS.length;
56
    mRotationRow = new float[mNumAxis];
57
    computeRotationRow();
58
    }
59

    
60
///////////////////////////////////////////////////////////////////////////////////////////////////
61
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
62
// correct the value of the 'scramble' quat to what it should be - one of the legal quats from the
63
// list LEGAL_QUATS.
64
//
65
// We also have to remember that the group of unit quaternions is a double-cover of rotations
66
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
67
/*
68
  private void normalizeScrambleQuat(Static4D quat)
69
    {
70
    final float MAX_ERROR = 0.0001f;
71

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

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

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

    
122
    quat.set(x,y,z,w);
123
    }
124
*/
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  private void modifyCurrentPosition(Static4D quat)
128
    {
129
    float cubitCenterX = mCurrentPosition.get0();
130
    float cubitCenterY = mCurrentPosition.get1();
131
    float cubitCenterZ = mCurrentPosition.get2();
132

    
133
    Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
134
    Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
135

    
136
    float rotatedX = rotatedCenter.get0();
137
    float rotatedY = rotatedCenter.get1();
138
    float rotatedZ = rotatedCenter.get2();
139

    
140
    mCurrentPosition.set(rotatedX, rotatedY, rotatedZ);
141
    mParent.clampPos(mCurrentPosition);
142

    
143
    computeRotationRow();
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147
// cast current position on axis; use mStart and mStep to compute the rotation row for each axis.
148

    
149
  private void computeRotationRow()
150
    {
151
    float tmp;
152
    Static3D axis;
153
    float x = mCurrentPosition.get0();
154
    float y = mCurrentPosition.get1();
155
    float z = mCurrentPosition.get2();
156

    
157
    for(int i=0; i<mNumAxis; i++)
158
      {
159
      axis = mParent.ROTATION_AXIS[i];
160
      tmp = x*axis.get0() + y*axis.get1() + z*axis.get2();
161
      mRotationRow[i] = (tmp-mParent.mStart)/mParent.mStep;
162
      }
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
  int computeAssociation()
168
    {
169
    int row, result = 0, accumulativeShift = 0;
170

    
171
    for(int axis=0; axis<mNumAxis; axis++)
172
      {
173
      row = (int)(mRotationRow[axis]+0.5f);
174
      result += (1<<(row+accumulativeShift));
175
      accumulativeShift += RubikObjectList.MAX_OBJECT_SIZE;
176
      }
177

    
178
    return result;
179
    }
180

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

    
183
  void savePreferences(SharedPreferences.Editor editor)
184
    {
185
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
186

    
187
    editor.putFloat("qx_"+number, mQuatScramble.get0());
188
    editor.putFloat("qy_"+number, mQuatScramble.get1());
189
    editor.putFloat("qz_"+number, mQuatScramble.get2());
190
    editor.putFloat("qw_"+number, mQuatScramble.get3());
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194

    
195
  void restorePreferences(SharedPreferences preferences)
196
    {
197
    String number = mOrigPosition.get0()+"_"+mOrigPosition.get1()+"_"+mOrigPosition.get2();
198

    
199
    float qx = preferences.getFloat("qx_"+number, 0.0f);
200
    float qy = preferences.getFloat("qy_"+number, 0.0f);
201
    float qz = preferences.getFloat("qz_"+number, 0.0f);
202
    float qw = preferences.getFloat("qw_"+number, 1.0f);
203

    
204
    mQuatScramble.set(qx,qy,qz,qw);
205
    modifyCurrentPosition(mQuatScramble);
206
    }
207

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

    
218
  boolean thereIsNoVisibleDifference(Static4D quat)
219
    {
220
    if ( mQuatScramble.get0()==quat.get0() &&
221
         mQuatScramble.get1()==quat.get1() &&
222
         mQuatScramble.get2()==quat.get2() &&
223
         mQuatScramble.get3()==quat.get3()  ) return true;
224

    
225
    int belongsToHowManyFaces = 0;
226
    int size = mParent.getSize()-1;
227
    float row;
228
    final float MAX_ERROR = 0.01f;
229

    
230
    for(int i=0; i<mNumAxis; i++)
231
      {
232
      row = mRotationRow[i];
233
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
234
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
235
      }
236

    
237
    switch(belongsToHowManyFaces)
238
      {
239
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
240
      case 1 :                // cubit that lies inside one of the faces
241
               float cubitCenterX = mCurrentPosition.get0();
242
               float cubitCenterY = mCurrentPosition.get1();
243
               float cubitCenterZ = mCurrentPosition.get2();
244

    
245
               Static4D cubitCenter = new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
246
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
247
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, mQuatScramble );
248

    
249
               float row1, row2, row3, row4;
250
               float ax,ay,az;
251
               Static3D axis;
252
               float x1 = rotated1.get0();
253
               float y1 = rotated1.get1();
254
               float z1 = rotated1.get2();
255
               float x2 = rotated2.get0();
256
               float y2 = rotated2.get1();
257
               float z2 = rotated2.get2();
258

    
259
               for(int i=0; i<mNumAxis; i++)
260
                 {
261
                 axis = mParent.ROTATION_AXIS[i];
262
                 ax = axis.get0();
263
                 ay = axis.get1();
264
                 az = axis.get2();
265

    
266
                 row1 = ((x1*ax + y1*ay + z1*az) - mParent.mStart) / mParent.mStep;
267
                 row2 = ((x2*ax + y2*ay + z2*az) - mParent.mStart) / mParent.mStep;
268
                 row3 = row1 - size;
269
                 row4 = row2 - size;
270

    
271
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
272
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
273
                   {
274
                   return true;
275
                   }
276
                 }
277
               return false;
278
      default: return false;  // edge or corner
279
      }
280
    }
281

    
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283

    
284
  void removeRotationNow(Static4D quat)
285
    {
286
    mQuatScramble.set(RubikSurfaceView.quatMultiply(quat,mQuatScramble));
287
 //   normalizeScrambleQuat( mQuatScramble );
288
    modifyCurrentPosition(quat);
289
    }
290

    
291
///////////////////////////////////////////////////////////////////////////////////////////////////
292

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

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
  float getDistSquared(float[] point)
303
    {
304
    float dx = mCurrentPosition.get0() - point[0];
305
    float dy = mCurrentPosition.get1() - point[1];
306
    float dz = mCurrentPosition.get2() - point[2];
307

    
308
    return dx*dx + dy*dy + dz*dz;
309
    }
310
}
(1-1/8)