Revision 05fa94d9
Added by Leszek Koltunski almost 5 years ago
src/main/java/org/distorted/magic/RubikCube.java | ||
---|---|---|
53 | 53 |
private static final Static3D VectY = new Static3D(0,1,0); |
54 | 54 |
private static final Static3D VectZ = new Static3D(0,0,1); |
55 | 55 |
|
56 |
private static final float SQ2 = 0.5f*((float)Math.sqrt(2)); |
|
57 |
private static final float[] LEGAL = { 0.0f , 0.5f , -0.5f , 1.0f , -1.0f , SQ2 , -SQ2 }; |
|
58 |
|
|
56 | 59 |
public static final int VECTX = 0; // |
57 | 60 |
public static final int VECTY = 1; // don't change this |
58 | 61 |
public static final int VECTZ = 2; // |
... | ... | |
185 | 188 |
} |
186 | 189 |
} |
187 | 190 |
|
191 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
192 |
|
|
193 |
boolean isSolved() |
|
194 |
{ |
|
195 |
Static4D q = mQuatScramble[0][0][0]; |
|
196 |
|
|
197 |
float x = q.get1(); |
|
198 |
float y = q.get2(); |
|
199 |
float z = q.get3(); |
|
200 |
float w = q.get4(); |
|
201 |
|
|
202 |
for(int i = 0; i< mSize; i++) |
|
203 |
for(int j = 0; j< mSize; j++) |
|
204 |
for(int k = 0; k< mSize; k++) |
|
205 |
{ |
|
206 |
if( i==0 || i==mSize-1 || j==0 || j==mSize-1 || k==0 || k==mSize-1 ) |
|
207 |
{ |
|
208 |
q = mQuatScramble[i][j][k]; |
|
209 |
|
|
210 |
if( q.get1()!=x || q.get2()!=y || q.get3()!=z || q.get4()!=w ) |
|
211 |
{ |
|
212 |
return false; |
|
213 |
} |
|
214 |
} |
|
215 |
} |
|
216 |
|
|
217 |
return true; |
|
218 |
} |
|
219 |
|
|
188 | 220 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
189 | 221 |
|
190 | 222 |
public void apply(Effect effect, int position) |
... | ... | |
498 | 530 |
|
499 | 531 |
mRotationAngle[x][y][z].removeAll(); |
500 | 532 |
mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z])); |
533 |
normalizeScrambleQuat(x,y,z); |
|
501 | 534 |
modifyCurrentPosition(x,y,z,quat); |
502 | 535 |
} |
503 | 536 |
} |
... | ... | |
505 | 538 |
mRotationAngleStatic.set1(0); |
506 | 539 |
} |
507 | 540 |
|
541 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
542 |
// All legal rotation quats must have all four of their components equal to either |
|
543 |
// 0, 1, -1, 0.5, -0.5 or +-sqrt(2)/2. |
|
544 |
// |
|
545 |
// Because of quatMultiplication, errors can accumulate - so to avoid this, we |
|
546 |
// correct the value of the 'scramble' quat to what it should be. |
|
547 |
// |
|
548 |
// We also have to remember that the group of unit quaternions is a double-cover of rotations |
|
549 |
// in 3D ( q represents the same rotation as -q ) - so invert if needed. |
|
550 |
|
|
551 |
private void normalizeScrambleQuat(int i, int j, int k) |
|
552 |
{ |
|
553 |
Static4D quat = mQuatScramble[i][j][k]; |
|
554 |
|
|
555 |
float x = quat.get1(); |
|
556 |
float y = quat.get2(); |
|
557 |
float z = quat.get3(); |
|
558 |
float w = quat.get4(); |
|
559 |
float diff; |
|
560 |
|
|
561 |
for(int legal=0; legal<LEGAL.length; legal++) |
|
562 |
{ |
|
563 |
diff = x-LEGAL[legal]; |
|
564 |
if( diff*diff<0.01f ) x = LEGAL[legal]; |
|
565 |
diff = y-LEGAL[legal]; |
|
566 |
if( diff*diff<0.01f ) y = LEGAL[legal]; |
|
567 |
diff = z-LEGAL[legal]; |
|
568 |
if( diff*diff<0.01f ) z = LEGAL[legal]; |
|
569 |
diff = w-LEGAL[legal]; |
|
570 |
if( diff*diff<0.01f ) w = LEGAL[legal]; |
|
571 |
} |
|
572 |
|
|
573 |
if( w<0 ) |
|
574 |
{ |
|
575 |
w = -w; |
|
576 |
z = -z; |
|
577 |
y = -y; |
|
578 |
x = -x; |
|
579 |
} |
|
580 |
else if( w==0 ) |
|
581 |
{ |
|
582 |
if( z<0 ) |
|
583 |
{ |
|
584 |
z = -z; |
|
585 |
y = -y; |
|
586 |
x = -x; |
|
587 |
} |
|
588 |
else if( z==0 ) |
|
589 |
{ |
|
590 |
if( y<0 ) |
|
591 |
{ |
|
592 |
y = -y; |
|
593 |
x = -x; |
|
594 |
} |
|
595 |
else if( y==0 ) |
|
596 |
{ |
|
597 |
if( x<0 ) |
|
598 |
{ |
|
599 |
x = -x; |
|
600 |
} |
|
601 |
} |
|
602 |
} |
|
603 |
} |
|
604 |
|
|
605 |
mQuatScramble[i][j][k].set(x,y,z,w); |
|
606 |
} |
|
607 |
|
|
508 | 608 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
509 | 609 |
|
510 | 610 |
private float getSinkStrength() |
src/main/java/org/distorted/magic/RubikRenderer.java | ||
---|---|---|
47 | 47 |
private int mNextCubeSize, mScrambleCubeNum; |
48 | 48 |
private long mRotationFinishedID; |
49 | 49 |
private long[] mEffectID; |
50 |
private boolean mFinishRotation, mRemoveRotation, mFinishDragCurrent, mFinishDragAccumulated;
|
|
50 |
private boolean mFinishRotation, mRemoveRotation, mSetQuatCurrent, mSetQuatAccumulated;
|
|
51 | 51 |
private boolean mSizeChangeCube, mSolveCube, mScrambleCube; |
52 | 52 |
private boolean mCanRotate, mCanDrag, mCanUI; |
53 | 53 |
private RubikCube mOldCube, mNewCube; |
... | ... | |
68 | 68 |
mScreenWidth = mScreenHeight = 0; |
69 | 69 |
mScrambleCubeNum = 0; |
70 | 70 |
|
71 |
mFinishRotation = false;
|
|
72 |
mRemoveRotation = false;
|
|
73 |
mFinishDragCurrent = false;
|
|
74 |
mFinishDragAccumulated = false;
|
|
75 |
mSizeChangeCube = false;
|
|
76 |
mSolveCube = false;
|
|
77 |
mScrambleCube = false;
|
|
71 |
mFinishRotation = false; |
|
72 |
mRemoveRotation = false; |
|
73 |
mSetQuatCurrent = false;
|
|
74 |
mSetQuatAccumulated = false;
|
|
75 |
mSizeChangeCube = true;
|
|
76 |
mSolveCube = false; |
|
77 |
mScrambleCube = false; |
|
78 | 78 |
|
79 | 79 |
mCanRotate = true; |
80 | 80 |
mCanDrag = true; |
... | ... | |
83 | 83 |
mEffectID = new long[BaseEffect.Type.LENGTH]; |
84 | 84 |
|
85 | 85 |
mMesh= new MeshFlat(20,20); |
86 |
mNextCubeSize =RubikActivity.getSize(); |
|
86 |
mNextCubeSize = RubikActivity.getSize();
|
|
87 | 87 |
} |
88 | 88 |
|
89 | 89 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
95 | 95 |
{ |
96 | 96 |
mScreen.render( System.currentTimeMillis() ); |
97 | 97 |
|
98 |
if( mFinishDragCurrent )
|
|
98 |
if( mSetQuatCurrent )
|
|
99 | 99 |
{ |
100 |
mFinishDragCurrent = false;
|
|
100 |
mSetQuatCurrent = false;
|
|
101 | 101 |
mView.setQuatCurrent(); |
102 | 102 |
} |
103 | 103 |
|
104 |
if( mFinishDragAccumulated )
|
|
104 |
if( mSetQuatAccumulated )
|
|
105 | 105 |
{ |
106 |
mFinishDragAccumulated = false;
|
|
106 |
mSetQuatAccumulated = false;
|
|
107 | 107 |
mView.setQuatAccumulated(); |
108 | 108 |
} |
109 | 109 |
|
... | ... | |
118 | 118 |
{ |
119 | 119 |
mRemoveRotation=false; |
120 | 120 |
mNewCube.removeRotationNow(); |
121 |
|
|
122 |
if( mNewCube.isSolved() ) |
|
123 |
{ |
|
124 |
android.util.Log.e("renderer", "CUBE IS SOLVED NOW"); |
|
125 |
} |
|
126 |
|
|
121 | 127 |
mCanRotate = true; |
122 | 128 |
} |
123 | 129 |
|
... | ... | |
127 | 133 |
mCanDrag = false; |
128 | 134 |
mCanRotate = false; |
129 | 135 |
createCubeNow(mNextCubeSize); |
130 |
doEffectNow(0);
|
|
136 |
doEffectNow( BaseEffect.Type.SIZECHANGE );
|
|
131 | 137 |
} |
132 | 138 |
|
133 | 139 |
if( mSolveCube ) |
... | ... | |
136 | 142 |
mCanDrag = false; |
137 | 143 |
mCanRotate = false; |
138 | 144 |
mCanUI = false; |
139 |
doEffectNow(1);
|
|
145 |
doEffectNow( BaseEffect.Type.SOLVE );
|
|
140 | 146 |
} |
141 | 147 |
|
142 | 148 |
if( mScrambleCube ) |
... | ... | |
145 | 151 |
mCanDrag = false; |
146 | 152 |
mCanRotate = false; |
147 | 153 |
mCanUI = false; |
148 |
doEffectNow(2);
|
|
154 |
doEffectNow( BaseEffect.Type.SCRAMBLE );
|
|
149 | 155 |
} |
150 | 156 |
} |
151 | 157 |
|
... | ... | |
296 | 302 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
297 | 303 |
// do all 'adjustable' effects (SizeChange, Solve, Scramble) |
298 | 304 |
|
299 |
private void doEffectNow(int index)
|
|
305 |
private void doEffectNow(BaseEffect.Type type)
|
|
300 | 306 |
{ |
301 |
mEffectID[index] = BaseEffect.Type.getType(index).startEffect(this); |
|
307 |
int index = type.ordinal(); |
|
308 |
|
|
309 |
mEffectID[index] = type.startEffect(this); |
|
302 | 310 |
|
303 | 311 |
if( mEffectID[index] == -1 ) |
304 | 312 |
{ |
... | ... | |
361 | 369 |
|
362 | 370 |
void setQuatCurrentOnNextRender() |
363 | 371 |
{ |
364 |
mFinishDragCurrent = true;
|
|
372 |
mSetQuatCurrent = true;
|
|
365 | 373 |
} |
366 | 374 |
|
367 | 375 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
368 | 376 |
|
369 | 377 |
void setQuatAccumulatedOnNextRender() |
370 | 378 |
{ |
371 |
mFinishDragAccumulated = true;
|
|
379 |
mSetQuatAccumulated = true;
|
|
372 | 380 |
} |
373 | 381 |
} |
Also available in: Unified diff
RubikCube: detect if the cube is solved.
In order to do this correctly, we also needed to keep correcting each mQuatScramble quaternions after each quatMultiplication in order to avoid multiplication errors accumulating. This turns out to be easy, because each quaternion representing a legal combination of rotations of a RubikCube must have each of the 4 to its components be equal to one of only 7 possible floats.