| 25 |
25 |
import android.opengl.GLSurfaceView;
|
| 26 |
26 |
import android.view.MotionEvent;
|
| 27 |
27 |
|
|
28 |
import org.distorted.library.type.Static3D;
|
| 28 |
29 |
import org.distorted.library.type.Static4D;
|
| 29 |
30 |
|
| 30 |
31 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
| 31 |
32 |
|
| 32 |
33 |
class RubikSurfaceView extends GLSurfaceView
|
| 33 |
34 |
{
|
| 34 |
|
private final static int NONE =-1;
|
|
35 |
private final static int NONE =-1;
|
| 35 |
36 |
private final static int FRONT = 0;
|
| 36 |
37 |
private final static int BACK = 1;
|
| 37 |
38 |
private final static int LEFT = 2;
|
| ... | ... | |
| 39 |
40 |
private final static int TOP = 4;
|
| 40 |
41 |
private final static int BOTTOM = 5;
|
| 41 |
42 |
|
| 42 |
|
private boolean mDragging;
|
|
43 |
private static final int DIR_UP =0;
|
|
44 |
private static final int DIR_DOWN =1;
|
|
45 |
private static final int DIR_LEFT =2;
|
|
46 |
private static final int DIR_RIGHT=3;
|
|
47 |
|
|
48 |
private static final Static3D VECTX = new Static3D(1,0,0);
|
|
49 |
private static final Static3D VECTY = new Static3D(0,1,0);
|
|
50 |
private static final Static3D VECTZ = new Static3D(0,0,1);
|
|
51 |
|
|
52 |
private boolean mDragging, mRotating;
|
| 43 |
53 |
private int mX, mY;
|
| 44 |
54 |
private int mTouchedRow, mTouchedCol, mTouchedSli;
|
| 45 |
55 |
private Static4D mQuatCurrent, mQuatAccumulated;
|
| 46 |
56 |
private RubikRenderer mRenderer;
|
| 47 |
57 |
|
|
58 |
private float mPoiX, mPoiY, mPoiZ, mCamX, mCamY, mCamZ;
|
|
59 |
private float mStartX, mStartY, mStartZ;
|
|
60 |
private int mLastTouchedFace;
|
|
61 |
|
| 48 |
62 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
| 49 |
63 |
|
| 50 |
64 |
public RubikSurfaceView(Context context)
|
| ... | ... | |
| 52 |
66 |
super(context);
|
| 53 |
67 |
|
| 54 |
68 |
mDragging = false;
|
|
69 |
mRotating = false;
|
| 55 |
70 |
mRenderer = new RubikRenderer(this);
|
| 56 |
71 |
|
| 57 |
72 |
mQuatCurrent = new Static4D(0,0,0,1);
|
| ... | ... | |
| 81 |
96 |
|
| 82 |
97 |
switch(action)
|
| 83 |
98 |
{
|
| 84 |
|
case MotionEvent.ACTION_DOWN: if( faceTouched(x,y) != NONE )
|
|
99 |
case MotionEvent.ACTION_DOWN: mX = x;
|
|
100 |
mY = y;
|
|
101 |
|
|
102 |
mLastTouchedFace = faceTouched(x,y);
|
|
103 |
|
|
104 |
if( mLastTouchedFace != NONE )
|
| 85 |
105 |
{
|
| 86 |
106 |
mRenderer.abortLastEffect();
|
| 87 |
107 |
mRenderer.applyNewEffect(mTouchedCol, mTouchedRow, mTouchedSli);
|
|
108 |
mRotating = true;
|
| 88 |
109 |
}
|
| 89 |
110 |
else
|
| 90 |
111 |
{
|
| 91 |
|
mX = x;
|
| 92 |
|
mY = y;
|
| 93 |
112 |
mDragging = true;
|
| 94 |
113 |
}
|
| 95 |
114 |
break;
|
| ... | ... | |
| 98 |
117 |
mQuatCurrent.set(quatFromDrag(mX-x,mY-y));
|
| 99 |
118 |
mRenderer.setQuatCurrent(mQuatCurrent);
|
| 100 |
119 |
}
|
|
120 |
else if( mRotating )
|
|
121 |
{
|
|
122 |
int minimumToRotate = (mRenderer.mScreenMin*mRenderer.mScreenMin)/36;
|
|
123 |
|
|
124 |
if( (mX-x)*(mX-x)+(mY-y)*(mY-y)>minimumToRotate )
|
|
125 |
{
|
|
126 |
rotateFace(x,y);
|
|
127 |
mRotating = false;
|
|
128 |
}
|
|
129 |
}
|
| 101 |
130 |
break;
|
| 102 |
131 |
case MotionEvent.ACTION_UP : mDragging = false;
|
|
132 |
mRotating = false;
|
|
133 |
|
| 103 |
134 |
mQuatAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
|
| 104 |
135 |
mQuatCurrent.set(0f, 0f, 0f, 1f);
|
| 105 |
|
|
| 106 |
136 |
mRenderer.setQuatCurrent(mQuatCurrent);
|
| 107 |
137 |
mRenderer.setQuatAccumulated(mQuatAccumulated);
|
| 108 |
138 |
break;
|
| ... | ... | |
| 111 |
141 |
return true;
|
| 112 |
142 |
}
|
| 113 |
143 |
|
|
144 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
145 |
|
|
146 |
private void rotateFace(int x, int y)
|
|
147 |
{
|
|
148 |
fillCameraAndTouchPoint(x,y);
|
|
149 |
float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
|
|
150 |
float A=0f;
|
|
151 |
|
|
152 |
switch(mLastTouchedFace)
|
|
153 |
{
|
|
154 |
case FRONT : A = ( mPoiZ!=mCamZ ? ( cubeHalfSize-mCamZ)/(mPoiZ-mCamZ) : 0f); break;
|
|
155 |
case BACK : A = ( mPoiZ!=mCamZ ? (-cubeHalfSize-mCamZ)/(mPoiZ-mCamZ) : 0f); break;
|
|
156 |
case LEFT : A = ( mPoiX!=mCamX ? (-cubeHalfSize-mCamX)/(mPoiX-mCamX) : 0f); break;
|
|
157 |
case RIGHT : A = ( mPoiX!=mCamX ? ( cubeHalfSize-mCamX)/(mPoiX-mCamX) : 0f); break;
|
|
158 |
case TOP : A = ( mPoiY!=mCamY ? ( cubeHalfSize-mCamY)/(mPoiY-mCamY) : 0f); break;
|
|
159 |
case BOTTOM: A = ( mPoiY!=mCamY ? (-cubeHalfSize-mCamY)/(mPoiY-mCamY) : 0f); break;
|
|
160 |
}
|
|
161 |
float diffX = (mPoiX-mCamX)*A + mCamX - mStartX;
|
|
162 |
float diffY = (mPoiY-mCamY)*A + mCamY - mStartY;
|
|
163 |
float diffZ = (mPoiZ-mCamZ)*A + mCamZ - mStartZ;
|
|
164 |
|
|
165 |
Static3D rotV=VECTX;
|
|
166 |
int dir, rotA =1, rotRC=1;
|
|
167 |
|
|
168 |
switch(mLastTouchedFace)
|
|
169 |
{
|
|
170 |
case FRONT : dir = retDirection( diffX, diffY);
|
|
171 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTY;
|
|
172 |
rotA = (dir==DIR_UP || dir==DIR_LEFT) ? -90:90;
|
|
173 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTX?mStartX:mStartY)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
174 |
break;
|
|
175 |
case BACK : dir = retDirection(-diffX, diffY);
|
|
176 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTY;
|
|
177 |
rotA = (dir==DIR_UP || dir==DIR_RIGHT) ? 90:-90;
|
|
178 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTX?mStartX:mStartY)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
179 |
break;
|
|
180 |
case LEFT : dir = retDirection( diffZ, diffY);
|
|
181 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTZ:VECTY;
|
|
182 |
rotA = (dir==DIR_UP || dir==DIR_LEFT) ? -90:90;
|
|
183 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTZ?mStartZ:mStartY)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
184 |
break;
|
|
185 |
case RIGHT : dir = retDirection(-diffZ, diffY);
|
|
186 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTZ:VECTY;
|
|
187 |
rotA = (dir==DIR_UP || dir==DIR_RIGHT) ? 90:-90;
|
|
188 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTZ?mStartZ:mStartY)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
189 |
break;
|
|
190 |
case TOP : dir = retDirection( diffX,-diffZ);
|
|
191 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTZ;
|
|
192 |
rotA = (dir==DIR_UP || dir==DIR_RIGHT) ? -90:90;
|
|
193 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTX?mStartX:mStartZ)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
194 |
break;
|
|
195 |
case BOTTOM: dir = retDirection( diffX, diffZ);
|
|
196 |
rotV = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTZ;
|
|
197 |
rotA = (dir==DIR_UP || dir==DIR_LEFT) ? -90:90;
|
|
198 |
rotRC = (int)( RubikRenderer.NUM_CUBES*((rotV==VECTX?mStartX:mStartZ)+cubeHalfSize)/(2*cubeHalfSize) );
|
|
199 |
break;
|
|
200 |
}
|
|
201 |
|
|
202 |
//if( rotRC==cubeHalfSize+1 ) rotRC=cubeHalfSize;
|
|
203 |
|
|
204 |
String vect = (rotV==VECTX ? "X" : (rotV==VECTY? "Y":"Z") );
|
|
205 |
|
|
206 |
android.util.Log.e("rubik", "rotating by "+rotA+" degrees, face="+rotRC+" vector: "+vect);
|
|
207 |
}
|
|
208 |
|
|
209 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
210 |
|
|
211 |
private int retDirection(float a, float b)
|
|
212 |
{
|
|
213 |
if( b>a ) return a>-b ? DIR_UP :DIR_LEFT;
|
|
214 |
else return a>-b ? DIR_RIGHT:DIR_DOWN;
|
|
215 |
}
|
|
216 |
|
| 114 |
217 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
| 115 |
218 |
// return quat1*quat2
|
| 116 |
219 |
|
| ... | ... | |
| 221 |
324 |
|
| 222 |
325 |
private int faceTouched(int xTouch, int yTouch)
|
| 223 |
326 |
{
|
| 224 |
|
float cameraDistance = mRenderer.returnCameraDistance();
|
| 225 |
|
float halfScrWidth = mRenderer.getScreenWidth()*0.5f;
|
| 226 |
|
float halfScrHeight = mRenderer.getScreenHeight()*0.5f;
|
| 227 |
|
float cubeHalfSize = mRenderer.returnCubeSize()*0.5f;
|
|
327 |
float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
|
| 228 |
328 |
|
| 229 |
|
Static4D cameraPoint = new Static4D( 0, 0, cameraDistance, 0);
|
| 230 |
|
Static4D touchPoint = new Static4D(xTouch-halfScrWidth, halfScrHeight-yTouch, 0, 0);
|
| 231 |
|
|
| 232 |
|
Static4D rotatedCamera = rotateVector(cameraPoint);
|
| 233 |
|
Static4D rotatedTouchPoint= rotateVector(touchPoint);
|
| 234 |
|
|
| 235 |
|
float camX = rotatedCamera.get1();
|
| 236 |
|
float camY = rotatedCamera.get2();
|
| 237 |
|
float camZ = rotatedCamera.get3();
|
| 238 |
|
|
| 239 |
|
float poiX = rotatedTouchPoint.get1();
|
| 240 |
|
float poiY = rotatedTouchPoint.get2();
|
| 241 |
|
float poiZ = rotatedTouchPoint.get3();
|
|
329 |
fillCameraAndTouchPoint(xTouch,yTouch);
|
| 242 |
330 |
|
| 243 |
331 |
if( faceIsVisible(FRONT) )
|
| 244 |
332 |
{
|
| 245 |
|
if( poiZ!= camZ )
|
|
333 |
if( mPoiZ!= mCamZ )
|
| 246 |
334 |
{
|
| 247 |
|
float A = (cubeHalfSize-camZ)/(poiZ-camZ);
|
| 248 |
|
float X = (poiX-camX)*A + camX;
|
| 249 |
|
float Y = (poiY-camY)*A + camY;
|
| 250 |
|
float qX= (X+cubeHalfSize) / (2*cubeHalfSize);
|
| 251 |
|
float qY= (Y+cubeHalfSize) / (2*cubeHalfSize);
|
|
335 |
float A = (cubeHalfSize-mCamZ)/(mPoiZ-mCamZ);
|
|
336 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
337 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
338 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
339 |
float qX= (mStartX+cubeHalfSize) / (2*cubeHalfSize);
|
|
340 |
float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
|
| 252 |
341 |
|
| 253 |
342 |
if( qX<=1 && qX>=0 && qY<=1 && qY>=0 )
|
| 254 |
343 |
{
|
| ... | ... | |
| 261 |
350 |
}
|
| 262 |
351 |
if( faceIsVisible(BACK) )
|
| 263 |
352 |
{
|
| 264 |
|
if( poiZ!= camZ )
|
|
353 |
if( mPoiZ!= mCamZ )
|
| 265 |
354 |
{
|
| 266 |
|
float A = (-cubeHalfSize-camZ)/(poiZ-camZ);
|
| 267 |
|
float X = (poiX-camX)*A + camX;
|
| 268 |
|
float Y = (poiY-camY)*A + camY;
|
| 269 |
|
float qX= (X+cubeHalfSize) / (2*cubeHalfSize);
|
| 270 |
|
float qY= (Y+cubeHalfSize) / (2*cubeHalfSize);
|
|
355 |
float A = (-cubeHalfSize-mCamZ)/(mPoiZ-mCamZ);
|
|
356 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
357 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
358 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
359 |
float qX= (mStartX+cubeHalfSize) / (2*cubeHalfSize);
|
|
360 |
float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
|
| 271 |
361 |
|
| 272 |
362 |
if( qX<=1 && qX>=0 && qY<=1 && qY>=0 )
|
| 273 |
363 |
{
|
| ... | ... | |
| 280 |
370 |
}
|
| 281 |
371 |
if( faceIsVisible(LEFT) )
|
| 282 |
372 |
{
|
| 283 |
|
if( poiX!= camX )
|
|
373 |
if( mPoiX!= mCamX )
|
| 284 |
374 |
{
|
| 285 |
|
float A = (-cubeHalfSize-camX)/(poiX-camX);
|
| 286 |
|
float Y = (poiY-camY)*A + camY;
|
| 287 |
|
float Z = (poiZ-camZ)*A + camZ;
|
| 288 |
|
float qY= (Y+cubeHalfSize) / (2*cubeHalfSize);
|
| 289 |
|
float qZ= (Z+cubeHalfSize) / (2*cubeHalfSize);
|
|
375 |
float A = (-cubeHalfSize-mCamX)/(mPoiX-mCamX);
|
|
376 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
377 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
378 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
379 |
float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
|
|
380 |
float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
|
| 290 |
381 |
|
| 291 |
382 |
if( qZ<=1 && qZ>=0 && qY<=1 && qY>=0 )
|
| 292 |
383 |
{
|
| ... | ... | |
| 299 |
390 |
}
|
| 300 |
391 |
if( faceIsVisible(RIGHT) )
|
| 301 |
392 |
{
|
| 302 |
|
if( poiX!= camX )
|
|
393 |
if( mPoiX!= mCamX )
|
| 303 |
394 |
{
|
| 304 |
|
float A = (cubeHalfSize-camX)/(poiX-camX);
|
| 305 |
|
float Y = (poiY-camY)*A + camY;
|
| 306 |
|
float Z = (poiZ-camZ)*A + camZ;
|
| 307 |
|
float qY= (Y+cubeHalfSize) / (2*cubeHalfSize);
|
| 308 |
|
float qZ= (Z+cubeHalfSize) / (2*cubeHalfSize);
|
|
395 |
float A = (cubeHalfSize-mCamX)/(mPoiX-mCamX);
|
|
396 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
397 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
398 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
399 |
float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
|
|
400 |
float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
|
| 309 |
401 |
|
| 310 |
402 |
if( qZ<=1 && qZ>=0 && qY<=1 && qY>=0 )
|
| 311 |
403 |
{
|
| ... | ... | |
| 318 |
410 |
}
|
| 319 |
411 |
if( faceIsVisible(TOP) )
|
| 320 |
412 |
{
|
| 321 |
|
if( poiY!= camY )
|
|
413 |
if( mPoiY!= mCamY )
|
| 322 |
414 |
{
|
| 323 |
|
float A = (cubeHalfSize-camY)/(poiY-camY);
|
| 324 |
|
float X = (poiX-camX)*A + camX;
|
| 325 |
|
float Z = (poiZ-camZ)*A + camZ;
|
| 326 |
|
float qX= (X+cubeHalfSize) / (2*cubeHalfSize);
|
| 327 |
|
float qZ= (Z+cubeHalfSize) / (2*cubeHalfSize);
|
|
415 |
float A = (cubeHalfSize-mCamY)/(mPoiY-mCamY);
|
|
416 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
417 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
418 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
419 |
float qX= (mStartX+cubeHalfSize) / (2*cubeHalfSize);
|
|
420 |
float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
|
| 328 |
421 |
|
| 329 |
422 |
if( qZ<=1 && qZ>=0 && qX<=1 && qX>=0 )
|
| 330 |
423 |
{
|
| ... | ... | |
| 337 |
430 |
}
|
| 338 |
431 |
if( faceIsVisible(BOTTOM) )
|
| 339 |
432 |
{
|
| 340 |
|
if( poiY!= camY )
|
|
433 |
if( mPoiY!= mCamY )
|
| 341 |
434 |
{
|
| 342 |
|
float A = (-cubeHalfSize-camY)/(poiY-camY);
|
| 343 |
|
float X = (poiX-camX)*A + camX;
|
| 344 |
|
float Z = (poiZ-camZ)*A + camZ;
|
| 345 |
|
float qX= (X+cubeHalfSize) / (2*cubeHalfSize);
|
| 346 |
|
float qZ= (Z+cubeHalfSize) / (2*cubeHalfSize);
|
|
435 |
float A = (-cubeHalfSize-mCamY)/(mPoiY-mCamY);
|
|
436 |
mStartX = (mPoiX-mCamX)*A + mCamX;
|
|
437 |
mStartY = (mPoiY-mCamY)*A + mCamY;
|
|
438 |
mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
|
|
439 |
float qX= (mStartX+cubeHalfSize) / (2*cubeHalfSize);
|
|
440 |
float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
|
| 347 |
441 |
|
| 348 |
442 |
if( qZ<=1 && qZ>=0 && qX<=1 && qX>=0 )
|
| 349 |
443 |
{
|
| ... | ... | |
| 361 |
455 |
|
| 362 |
456 |
return NONE;
|
| 363 |
457 |
}
|
|
458 |
|
|
459 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
460 |
|
|
461 |
void fillCameraAndTouchPoint(int x, int y)
|
|
462 |
{
|
|
463 |
float cameraDistance = mRenderer.returnCameraDistance();
|
|
464 |
float halfScrWidth = mRenderer.getScreenWidth()*0.5f;
|
|
465 |
float halfScrHeight = mRenderer.getScreenHeight()*0.5f;
|
|
466 |
|
|
467 |
Static4D cameraPoint = new Static4D( 0, 0, cameraDistance, 0);
|
|
468 |
Static4D touchPoint = new Static4D(x-halfScrWidth, halfScrHeight-y, 0, 0);
|
|
469 |
|
|
470 |
Static4D rotatedCamera = rotateVector(cameraPoint);
|
|
471 |
Static4D rotatedTouchPoint= rotateVector(touchPoint);
|
|
472 |
|
|
473 |
mCamX = rotatedCamera.get1();
|
|
474 |
mCamY = rotatedCamera.get2();
|
|
475 |
mCamZ = rotatedCamera.get3();
|
|
476 |
|
|
477 |
mPoiX = rotatedTouchPoint.get1();
|
|
478 |
mPoiY = rotatedTouchPoint.get2();
|
|
479 |
mPoiZ = rotatedTouchPoint.get3();
|
|
480 |
}
|
| 364 |
481 |
}
|
| 365 |
482 |
|
Improve the Rubik App.