27 |
27 |
private final TwistyObjectControllable mObject;
|
28 |
28 |
private final int[][] mBasicAngles;
|
29 |
29 |
private final int mNumCubits;
|
30 |
|
private final int mGhostAngle;
|
31 |
30 |
private final Static3D[] mAxis;
|
32 |
31 |
private final Static4D[] mObjectQuats;
|
33 |
32 |
private final int mNumAxis;
|
34 |
33 |
private final int mNumQuats;
|
|
34 |
private final int[] mNumLayers;
|
|
35 |
private final int mMaxNumLayers;
|
|
36 |
|
|
37 |
private final int mGhostAngle; // defined
|
|
38 |
private final int mGhostOrigAxis; // defined
|
|
39 |
private final int mGhostFirstRow; // defined
|
|
40 |
private int mGhostAxis; // if Ghost is not blocked, those two are undefined.
|
|
41 |
private boolean mGhostAxisInverted; // Otherwise, this is the axis and its direction along which the
|
|
42 |
// puzzle is blocked (!inverted -> axis agrees with rotAxis)
|
|
43 |
private boolean mGhostBlocked; // defined
|
|
44 |
private int mGhostRowBitmap; // defined
|
35 |
45 |
|
36 |
46 |
private VertexEffectRotate mGhostEffect;
|
37 |
|
private int mGhostNumRows;
|
38 |
|
private int mGhostBasicDeg;
|
39 |
47 |
private Static1D mGhostStatic;
|
40 |
48 |
private Static3D mGhostEffectAxis;
|
41 |
49 |
private int[][][] mGhostAxisJumpedTo;
|
42 |
50 |
private boolean[] mGhostCubitRotated, mGhostOrigRotated;
|
43 |
51 |
private Static4D mGhostQuat;
|
44 |
|
private int mMaxNumLayers;
|
45 |
|
|
46 |
|
private int mGhostAxis; // the index of the axis of rotations along which the ghost
|
47 |
|
// can always move.
|
48 |
|
private int mGhostFirstRow; // Every ghost has two kinds of rows along its mGhostAxis:
|
49 |
|
// 'left' ones (normal) and 'right' ones (blocking). FirstRow is
|
50 |
|
// the number of the first right row.
|
51 |
|
private boolean mGhostBlocked; // if true, all rotations along axis different than mGhostAxis
|
52 |
|
// are blocked.
|
53 |
|
private int mGhostRowBitmap; // a bitmap; if bit[N] is 1, this means that layer N along the
|
54 |
|
// mGhostAxis is ghostRotated (initially all layers left of
|
55 |
|
// mGhostFirstRow are not and every ones right and including
|
56 |
|
// mGhostFirstRow are)
|
57 |
|
private boolean mGhostAxisInverted; // we also care about the direction of the ghostAxis. It can
|
58 |
|
// change, for example a 2x2x2 has 3 rotAxes, but its ghostAxis
|
59 |
|
// can point in 6 directions (3 axes, each inverted or not)
|
60 |
52 |
|
61 |
53 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
62 |
54 |
|
... | ... | |
69 |
61 |
mObjectQuats= controllable.getQuats();
|
70 |
62 |
mNumAxis = mAxis.length;
|
71 |
63 |
mNumQuats = mObjectQuats.length;
|
|
64 |
mNumLayers = controllable.getNumLayers();
|
72 |
65 |
|
73 |
66 |
DistortedEffects effects = controllable.getObjectEffects();
|
74 |
|
int[] numLayers = controllable.getNumLayers();
|
75 |
67 |
|
76 |
|
for(int numLayer : numLayers)
|
77 |
|
if( mMaxNumLayers<numLayer ) mMaxNumLayers = numLayer;
|
|
68 |
int maxNumLayers = 0;
|
|
69 |
for(int numLayer : mNumLayers)
|
|
70 |
if( maxNumLayers<numLayer ) maxNumLayers = numLayer;
|
|
71 |
mMaxNumLayers = maxNumLayers;
|
78 |
72 |
|
79 |
|
mGhostAngle = controllable.getGhostAngle();
|
|
73 |
mGhostAngle = controllable.getGhostAngle();
|
|
74 |
mGhostOrigAxis = controllable.getGhostAxis();
|
|
75 |
mGhostFirstRow = controllable.getGhostFirstRow();
|
80 |
76 |
|
81 |
77 |
if( mGhostAngle!=0 )
|
82 |
78 |
{
|
83 |
|
mGhostAxis = controllable.getGhostAxis();
|
84 |
|
mGhostFirstRow= controllable.getGhostFirstRow();
|
|
79 |
mGhostAxis = mGhostOrigAxis;
|
85 |
80 |
mGhostBlocked = true;
|
86 |
|
mGhostBasicDeg= 360/mBasicAngles[mGhostAxis][0];
|
87 |
|
mGhostNumRows = numLayers[mGhostAxis];
|
88 |
81 |
mGhostStatic = new Static1D(mGhostAngle);
|
89 |
82 |
|
90 |
83 |
Static3D currGhostAxis = mAxis[mGhostAxis];
|
... | ... | |
103 |
96 |
computeGhostAxisJumps();
|
104 |
97 |
|
105 |
98 |
mGhostAxisInverted = false;
|
106 |
|
int tmp = 1<<(mGhostNumRows-mGhostFirstRow);
|
|
99 |
int tmp = 1<<(mNumLayers[mGhostAxis]-mGhostFirstRow);
|
107 |
100 |
mGhostRowBitmap = (tmp-1)<<mGhostFirstRow;
|
108 |
101 |
|
109 |
102 |
reinitializeGhostStuff();
|
... | ... | |
112 |
105 |
}
|
113 |
106 |
|
114 |
107 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
115 |
|
// We are adding an automatic rotation [i.e. stepping through a solution, backing a move, scrambling]
|
116 |
|
// Do various ghost-related stuff, e.g. change (axis,row,angle) to unblock the ghost if it is blocked!
|
|
108 |
// is the ghost currently blocked along the 'axis' ?
|
117 |
109 |
|
118 |
|
void onAutomaticRotation()
|
119 |
|
{
|
120 |
|
if( mGhostAngle!=0 && mGhostBlocked )
|
121 |
|
{
|
122 |
|
/*
|
123 |
|
axis = mGhostAxis;
|
124 |
|
int tmp = 1<<(mGhostNumRows-mGhostFirstRow);
|
125 |
|
rowBitmap = (tmp-1)<<mGhostFirstRow;
|
126 |
|
angle -= mGhostAngle;
|
127 |
|
*/
|
128 |
|
}
|
129 |
|
}
|
|
110 |
boolean ghostBlocked(int axis) { return mGhostBlocked && axis!=mGhostAxis; }
|
130 |
111 |
|
131 |
112 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
132 |
113 |
// Called when finishing a rotation.
|
133 |
114 |
// Is 'ghostRotation' (rotating a row by a ghostAngle) possible along this axis?
|
134 |
115 |
|
135 |
|
boolean ghostRotationImpossible(int axis)
|
136 |
|
{
|
137 |
|
return (mGhostAngle==0 || axis!=mGhostAxis);
|
138 |
|
}
|
|
116 |
boolean ghostRotationImpossible(int axis) { return mGhostAngle==0; }
|
|
117 |
|
|
118 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
119 |
// We are adding an automatic rotation [i.e. stepping through a solution, backing a move, scrambling]
|
|
120 |
// Return (axis,rowBitmap,angle) so that the puzzle unblocks!
|
|
121 |
|
|
122 |
int unblockingAxis() { return mGhostAxis; }
|
|
123 |
int unblockingBitmap() { return (1<<(mNumLayers[mGhostAxis]-mGhostFirstRow))-1; }
|
|
124 |
int unblockingAngle(int angle) { return angle-mGhostAngle; }
|
139 |
125 |
|
140 |
126 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
141 |
127 |
// ghostRotation is possible, so return an rotAngle nearest to 'angle' taking into account
|
... | ... | |
172 |
158 |
{
|
173 |
159 |
int nonGhostAngle = (int)angle;
|
174 |
160 |
|
175 |
|
if( mGhostAngle!=0 )
|
|
161 |
if( mGhostAngle!=0 && (axis==mGhostAxis || !mGhostBlocked) )
|
176 |
162 |
{
|
177 |
|
if( (nonGhostAngle%mGhostBasicDeg)!=0 )
|
|
163 |
int basicDeg = 360/mBasicAngles[mGhostAxis][0];
|
|
164 |
|
|
165 |
if( (nonGhostAngle%basicDeg)!=0 )
|
178 |
166 |
{
|
179 |
|
if( ((nonGhostAngle-mGhostAngle)%mGhostBasicDeg == 0) ) nonGhostAngle -= mGhostAngle;
|
180 |
|
else if( ((nonGhostAngle+mGhostAngle)%mGhostBasicDeg == 0) ) nonGhostAngle += mGhostAngle;
|
181 |
|
else android.util.Log.e("D", "error in ghost remove rotation");
|
|
167 |
if( ((nonGhostAngle-mGhostAngle)%basicDeg == 0) ) nonGhostAngle -= mGhostAngle;
|
|
168 |
else if( ((nonGhostAngle+mGhostAngle)%basicDeg == 0) ) nonGhostAngle += mGhostAngle;
|
|
169 |
else android.util.Log.e("D", "error in ghost onRemoveRotation");
|
182 |
170 |
}
|
183 |
|
if( angle!=0 ) doGhostStuff(axis,rowBitmap,angle);
|
|
171 |
|
|
172 |
accountForGhostAngleOnRemoveRotation(axis,rowBitmap,angle);
|
184 |
173 |
}
|
185 |
174 |
|
186 |
175 |
return nonGhostAngle;
|
... | ... | |
189 |
178 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
190 |
179 |
// Called when removing a rotation, after all cubits have been rotated.
|
191 |
180 |
|
192 |
|
void onPostRemoveRotation()
|
193 |
|
{
|
194 |
|
computeGhostCubitRotated();
|
195 |
|
}
|
196 |
|
|
197 |
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
198 |
|
// solve the ghost part of the puzzle
|
199 |
|
|
200 |
|
void solve()
|
201 |
|
{
|
202 |
|
if( mGhostAngle!=0 )
|
203 |
|
{
|
204 |
|
mGhostAxis = mObject.getGhostAxis();
|
205 |
|
mGhostAxisInverted = false;
|
206 |
|
int tmp = 1<<(mGhostNumRows-mGhostFirstRow);
|
207 |
|
mGhostRowBitmap = (tmp-1)<<mGhostFirstRow;
|
208 |
|
reinitializeGhostStuff();
|
209 |
|
}
|
210 |
|
}
|
|
181 |
void onPostRemoveRotation() { computeGhostCubitRotated(); }
|
211 |
182 |
|
212 |
183 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
213 |
184 |
// Is the ghost part of the puzzle currently in a solved state?
|
... | ... | |
225 |
196 |
}
|
226 |
197 |
|
227 |
198 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
228 |
|
// is the ghost currently blocked, i.e. it can only move along the ghostAxis?
|
|
199 |
// solve the ghost part of the puzzle
|
229 |
200 |
|
230 |
|
boolean ghostBlocked(int axis) { return mGhostBlocked && axis!=mGhostAxis; }
|
|
201 |
void solve()
|
|
202 |
{
|
|
203 |
if( mGhostAngle!=0 )
|
|
204 |
{
|
|
205 |
mGhostAxis = mGhostOrigAxis;
|
|
206 |
mGhostAxisInverted = false;
|
|
207 |
int tmp = 1<<(mNumLayers[mGhostAxis]-mGhostFirstRow);
|
|
208 |
mGhostRowBitmap = (tmp-1)<<mGhostFirstRow;
|
|
209 |
reinitializeGhostStuff();
|
|
210 |
}
|
|
211 |
}
|
231 |
212 |
|
232 |
213 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
233 |
214 |
// read up from storage and restore the state of a ghost part of the puzzle.
|
... | ... | |
279 |
260 |
|
280 |
261 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
281 |
262 |
// PRIVATE
|
|
263 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
264 |
|
|
265 |
private void accountForGhostAngleOnRemoveRotation(int axis, int rowBitmap, float angle)
|
|
266 |
{
|
|
267 |
int basicDeg = 360/mBasicAngles[axis][0];
|
|
268 |
if( (angle%basicDeg)!=0 ) mGhostRowBitmap ^= rowBitmap;
|
|
269 |
|
|
270 |
if( mGhostBlocked && axis!=mGhostAxis ) computeNewGhostAxisAndBitmap(axis,angle);
|
|
271 |
|
|
272 |
int fullOnes = (1<<(mNumLayers[mGhostAxis]))-1;
|
|
273 |
mGhostBlocked = ( mGhostRowBitmap!=0 && mGhostRowBitmap!=fullOnes );
|
|
274 |
|
|
275 |
if( mGhostRowBitmap==fullOnes )
|
|
276 |
{
|
|
277 |
mGhostRowBitmap=0;
|
|
278 |
computeGhostQuaternion();
|
|
279 |
mObject.rotateCameraQuat(mGhostQuat);
|
|
280 |
}
|
|
281 |
|
|
282 |
mGhostEffect.setMeshAssociation(mGhostRowBitmap<<(mGhostAxis*mMaxNumLayers),-1);
|
|
283 |
}
|
|
284 |
|
282 |
285 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
283 |
286 |
|
284 |
287 |
private void reinitializeGhostStuff()
|
... | ... | |
291 |
294 |
|
292 |
295 |
mGhostEffect.setMeshAssociation(mGhostRowBitmap<<(mGhostAxis*mMaxNumLayers),-1);
|
293 |
296 |
|
294 |
|
int fullOnes = (1<<(mGhostNumRows))-1;
|
|
297 |
int fullOnes = (1<<(mNumLayers[mGhostAxis]))-1;
|
295 |
298 |
mGhostBlocked = ( mGhostRowBitmap!=0 && mGhostRowBitmap!=fullOnes );
|
296 |
299 |
|
297 |
300 |
mGhostStatic.set0(mGhostAxisInverted ? -mGhostAngle : mGhostAngle);
|
... | ... | |
437 |
440 |
return -1;
|
438 |
441 |
}
|
439 |
442 |
|
440 |
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
441 |
|
|
442 |
|
private void doGhostStuff(int axis, int rowBitmap, float angle)
|
443 |
|
{
|
444 |
|
if( axis==mGhostAxis && (angle%mGhostBasicDeg)!=0 ) mGhostRowBitmap ^= rowBitmap;
|
445 |
|
|
446 |
|
if( mGhostBlocked && axis!=mGhostAxis )
|
447 |
|
{
|
448 |
|
computeNewGhostAxisAndBitmap(axis,angle);
|
449 |
|
mGhostEffect.setMeshAssociation(mGhostRowBitmap<<(mGhostAxis*mMaxNumLayers),-1);
|
450 |
|
}
|
451 |
|
|
452 |
|
int fullOnes = (1<<(mGhostNumRows))-1;
|
453 |
|
mGhostBlocked = ( mGhostRowBitmap!=0 && mGhostRowBitmap!=fullOnes );
|
454 |
|
|
455 |
|
if( mGhostRowBitmap==fullOnes )
|
456 |
|
{
|
457 |
|
mGhostRowBitmap=0;
|
458 |
|
computeGhostQuaternion();
|
459 |
|
mObject.rotateCameraQuat(mGhostQuat);
|
460 |
|
}
|
461 |
|
|
462 |
|
mGhostEffect.setMeshAssociation(mGhostRowBitmap<<(mGhostAxis*mMaxNumLayers),-1);
|
463 |
|
}
|
464 |
|
|
465 |
443 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
466 |
444 |
// We have just rotated the whole blocked Ghost by 'angle' along 'axis'.
|
467 |
445 |
//
|
... | ... | |
492 |
470 |
mGhostEffectAxis.set(x,y,z);
|
493 |
471 |
|
494 |
472 |
if( (inverted==1)^mGhostAxisInverted ) invertGhostRotBitmap();
|
|
473 |
|
|
474 |
|
495 |
475 |
}
|
496 |
476 |
|
497 |
477 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
... | ... | |
503 |
483 |
|
504 |
484 |
int inve = 0;
|
505 |
485 |
int mask = 1;
|
|
486 |
int numr = mNumLayers[mGhostAxis];
|
506 |
487 |
|
507 |
|
for(int i=0; i<mGhostNumRows; i++)
|
|
488 |
for(int i=0; i<numr; i++)
|
508 |
489 |
{
|
509 |
|
if( (mGhostRowBitmap&mask) !=0 ) inve += (1<<(mGhostNumRows-i-1));
|
|
490 |
if( (mGhostRowBitmap&mask) !=0 ) inve += (1<<(numr-i-1));
|
510 |
491 |
mask *=2;
|
511 |
492 |
}
|
512 |
493 |
|
Ghost....