fix ebn warnings regarding includes
[kdegames.git] / kubrick / src / movetracker.cpp
blob773051ec54ecc1444c93682e71d43a96743e9e11
1 /*******************************************************************************
2 Copyright 2008 Ian Wadham <ianw2@optusnet.com.au>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *******************************************************************************/
19 #include "movetracker.h"
21 #include <math.h>
22 #include <stdlib.h>
23 #include <stdio.h>
25 #include <KDebug>
26 #include <KLocale>
27 #include <KMessageBox>
29 MoveTracker::MoveTracker (QWidget * parent)
31 QObject (parent),
32 myParent (parent)
34 init();
35 rotationState.quaternionSetIdentity();
36 rotationState.quaternionToMatrix (rotationMatrix);
40 MoveTracker::~MoveTracker()
45 void MoveTracker::init()
47 currentButton = Qt::NoButton; // No mouse button being pressed.
48 clickFace1 = false; // No face clicked by mouse button.
49 foundFace1 = false; // Starting face of move not found.
50 foundFace2 = false; // Finishing face of move not found.
51 foundHandle = false; // Rotation-handle not found.
52 moveAngle = 0; // No slice-move to be made (yet).
56 void MoveTracker::mouseInput (int sceneID, QList<CubeView *> cubeViews,
57 Cube * cube, MouseEvent event, int button, int mX, int mY)
59 if (event == ButtonDown) {
60 init();
61 currentButton = button;
64 if (currentButton == Qt::RightButton) {
65 // Right-button is down: rotate the whole cube.
66 trackCubeRotation (sceneID, cubeViews, event, mX, mY);
68 else if (currentButton == Qt::LeftButton) {
69 // Left-button is down: move a slice of the cube.
70 trackSliceMove (sceneID, cubeViews, cube, event, mX, mY);
73 if (event == ButtonUp) {
74 currentButton = Qt::NoButton;
79 void MoveTracker::trackCubeRotation (int sceneID, QList<CubeView *> cubeViews,
80 MouseEvent event, int mX, int mY)
82 if (foundHandle) {
83 // Move the handle-point to a new position while rotating the cube
84 // around its central point. The mouse-pointer appears to be attached
85 // to the handle-point as the cube rotates.
87 if ((mX == mX1) && (mY == mY1)) {
88 return; // No change in position of mouse.
90 mX1 = mX;
91 mY1 = mY;
93 double axis [nAxes] = {1.0, 0.0, 0.0};
94 double degrees = 0.0;
96 // Calculate the angle and axis of rotation.
98 // If the angle was effectively zero, avoid further calculation.
99 if (calculateRotation (mX, mY, axis, degrees)) {
100 // Else, add the rotation to the quaternion and the OpenGL matrix.
101 rotationState.quaternionAddRotation (axis, degrees);
102 rotationState.quaternionPrint(); // IDW
103 rotationState.quaternionToMatrix (rotationMatrix);
105 printf ("New handle: %7.3f %7.3f %7.3f R, RR %7.3f %7.3f %d %d\n",
106 handle[X], handle[Y], handle[Z], R, RR, mX, mY);
108 else if (event != ButtonUp) {
109 // Look for a handle-point: a point on the surface of a cube that can
110 // be used to rotate the cube around its central point. Any point will
111 // do, provided it is visible and on the surface of a cube.
113 GLfloat depth;
114 double position [nAxes];
116 // Get the mouse position in OpenGL world co-ordinates.
117 depth = getMousePosition (mX, mY, position);
118 printf ("GL coords: %7.2f %7.2f %7.2f %7.2f\n",
119 position[X], position[Y], position[Z], -maxZ+0.1);
121 // Find which picture of a cube the mouse is on.
122 int cubeID = findWhichCube (sceneID, cubeViews, position);
123 if (cubeID < 0) {
124 // Could not find the nearest cube (should never happen).
125 return;
127 v = cubeViews.at (cubeID);
129 if (position[Z] > (-maxZ + 0.1)) {
130 // Get the mouse position relative to the centre of the cube found.
131 // Use the transformation matrix for the translated and standardly
132 // aligned view of that cube, without including any user's rotation.
134 getGLPosition (mX, mY, depth, v->matrix0, handle);
135 RR = handle[X]*handle[X] + handle[Y]*handle[Y] +
136 handle[Z]*handle[Z];
137 R = sqrt (RR);
138 foundHandle = true;
139 mX1 = mX;
140 mY1 = mY;
141 printf ("Found handle: %7.3f %7.3f %7.3f R, RR %7.3f %7.3f %d %d\n",
142 handle[X], handle[Y], handle[Z], R, RR, mX, mY);
148 bool MoveTracker::calculateRotation (const int mX, const int mY,
149 double axis[], double & degrees)
151 bool result = true;
152 bool logging = false;
154 // Get two points on the line of sight, in the nearest cube's co-ordinates.
155 // Use the transformation matrix for the translated and standardly aligned
156 // view of that cube, without including any user's rotation.
158 double p1 [nAxes];
159 double p2 [nAxes];
161 // The "depth" in OpenGL is a normalised number in the range 0.0 to 1.0.
162 // We choose values 0.5 and 1.0 (background) for the two depths.
164 getGLPosition (mX, mY, 0.5, v->matrix0, p1);
165 getGLPosition (mX, mY, 1.0, v->matrix0, p2);
166 if (logging) {
167 printf ("Point1 GL coords: %7.2f %7.2f %7.2f Mouse at: %d %d\n",
168 p1[X], p1[Y], p1[Z], mX, mY);
169 printf ("Point2 GL coords: %7.2f %7.2f %7.2f\n", p2[X], p2[Y], p2[Z]);
172 // Find where the line of sight intersects the sphere containing the handle.
173 // To do this, we use the parametrised equation of a line between two
174 // points, i.e. p = p1 + lambda * (p2 - p1) for any point p. At those two
175 // points, px*px + py*py + pz*pz = R*R, which gives us a quadratic equation
176 // to solve for lambda: a*lambda*lambda + b*lambda +c = 0. So we calculate
177 // the coefficents a, b, and c.
179 double dx = (p2[X] - p1[X]);
180 double dy = (p2[Y] - p1[Y]);
181 double dz = (p2[Z] - p1[Z]);
182 double a = dx*dx + dy*dy + dz*dz;
183 double b = 2.0*(p1[X]*dx + p1[Y]*dy + p1[Z]*dz);
184 double c = p1[X]*p1[X] + p1[Y]*p1[Y] + p1[Z]*p1[Z] - RR;
186 // Apply the quadratic formula to solve for the nearest of the two lambdas.
187 double q = b*b - 4.0*a*c;
188 if (logging) {
189 printf ("a = %7.2f, b = %7.2f, c = %7.2f, b^2 - 4ac = %7.2f\n",
190 a, b, c, q);
192 double lambda = 0.0;
193 if (q >= 0.0) {
194 lambda = (-b - sqrt (q)) / (2.0*a);
196 else {
197 // The line of sight to the mouse pointer is outside the handle-sphere.
198 // The sphere must not turn any more, otherwise it flips unpredictably.
199 degrees = 0.0;
200 axis [X] = 1.0;
201 axis [Y] = 0.0;
202 axis [Z] = 0.0;
203 if (logging) {
204 printf ("The line of sight is outside the handle-sphere.\n");
206 return false;
208 if (logging) {
209 printf ("Lambda: %9.4f\n", lambda);
212 // Set up a vector for the old position on the handle-sphere.
213 double v1 [nAxes];
215 v1 [X] = handle [X];
216 v1 [Y] = handle [Y];
217 v1 [Z] = handle [Z];
219 // Set the new handle position on the handle-sphere.
220 handle [X] = (p1[X] + lambda*dx);
221 handle [Y] = (p1[Y] + lambda*dy);
222 handle [Z] = (p1[Z] + lambda*dz);
224 result = getTurnVector (v1, handle, axis, degrees);
226 if (logging) {
227 printf ("Vector v1: %7.3f, %7.3f, %7.3f\n", v1[X], v1[Y], v1[Z]);
228 printf ("Angle = %7.2f, axis = %7.2f, %7.2f, %7.2f\n",
229 degrees, axis[X], axis[Y], axis[Z]);
230 printf ("New handle: %7.2f, %7.2f, %7.2f\n",
231 handle[X], handle[Y], handle[Z]);
233 return result;
237 void MoveTracker::trackSliceMove (int sceneID, QList<CubeView *> cubeViews,
238 Cube * cube, MouseEvent event, int mX, int mY)
240 double position [nAxes];
242 if ((foundHandle) && ((mX != mX1) || (mY != mY1))) {
243 mX1 = mX;
244 mY1 = mY;
246 // Change the move axis and direction only when the mouse pointer moves
247 // away from the handle area or after it returns to it and moves away
248 // again. This prevents rapid switching and oscillation of the slices
249 // when the pointer is in a diagonal direction from the handle-point.
251 // IDW bool outsideHandleArea = (abs (mX - mX0) > 15) || (abs (mY - mY0) > 15);
252 bool outsideHandleArea = ((mX - mX0)*(mX - mX0) + (mY - mY0)*(mY - mY0))
253 > 400;
255 if ((moveAngle == 0) && outsideHandleArea) {
256 // Mouse just moved outside handle area: find the required move.
257 Axis direction = X;
258 double distance = 0.0;
260 // Get two points on line of sight, in the nearest cube's co-ords.
261 // Use the transformation matrix for the fully translated and
262 // rotated view of that cube, including any user's rotations.
264 double point1 [nAxes];
265 double point2 [nAxes];
267 // The "depth" in OpenGL is a normalised number in the range 0.0 to
268 // 1.0, so use values 0.5 and 1.0 (background) for the two depths.
270 getGLPosition (mX, mY, 0.5, v->matrix, point1);
271 getGLPosition (mX, mY, 1.0, v->matrix, point2);
273 // Find where the line of sight intersects the plane of the found
274 // face. To do this, use the parametrised equation of a line
275 // between two points: p = p2 + lambda * (p1 - p2) for any point p.
277 double plane = handle[noTurn];
278 double lambda = (plane - point2[noTurn]) /
279 (point1[noTurn] - point2[noTurn]);
280 LOOP (n, nAxes) {
281 if (n == noTurn) {
282 position[n] = plane;
284 else {
285 position[n] = point2[n] + lambda * (point1[n] - point2[n]);
289 // Find in which direction the mouse moved most.
290 kDebug() << "Mouse dX:" << (mX - mX0) << "dY:" << (mY - mY0);
291 LOOP (n, nAxes) {
292 if (n != noTurn) {
293 double d = position[n] - handle[n];
294 kDebug() << "Axis:" << n << "distance" << d;
295 if (fabs (d) > fabs (distance)) {
296 distance = d;
297 direction = (Axis) n;
302 // Turn axis will be at right angles to mouse move and face-normal.
303 currentMoveAxis = (((direction + 1) % nAxes) == noTurn) ?
304 (Axis) ((direction + 2) % nAxes) :
305 (Axis) ((direction + 1) % nAxes);
307 // Set up unit vectors for the mouse move and the face-normal.
308 int moveVec[nAxes] = {0, 0, 0};
309 int faceVec[nAxes] = {0, 0, 0};
310 moveVec[direction] = (distance < 0.0) ? -1 : +1;
311 faceVec[noTurn] = (face1[noTurn] < 0.0) ? -1 : +1;
313 // Form a partial cross-product to get the direction of the turn.
314 // The other components of turnVec must be zero (3 orthogonal axes).
316 int a = (currentMoveAxis + 1) % nAxes; // Get non-turn axes in
317 int b = (currentMoveAxis + 2) % nAxes; // cyclical order.
318 int turnVec = moveVec[a]*faceVec[b] - moveVec[b]*faceVec[a];
320 currentMoveDirection = (turnVec < 0) ? ANTICLOCKWISE : CLOCKWISE;
321 moveAngle = (turnVec < 0) ? -6 : 6;
323 currentMoveSlice = face1[currentMoveAxis];
324 cube->setMoveInProgress (currentMoveAxis, currentMoveSlice);
325 kDebug() << "a,b" << a << b << turnVec;
326 kDebug() << "Direction:" << direction << "distance:" << distance
327 << "axis:" << currentMoveAxis << "angle:" << moveAngle;
329 else if (! outsideHandleArea) {
330 moveAngle = 0;
333 cube->setMoveAngle (moveAngle); // If zero, no move will be triggered.
336 // Start by looking for a handle-point from which to make a slice move.
337 else if ((! foundHandle) && (event != ButtonUp)) {
338 // Get the mouse position in OpenGL world co-ordinates.
339 GLfloat depth;
340 depth = getMousePosition (mX, mY, position);
341 printf ("GL coords: %7.2f %7.2f %7.2f %7.2f\n",
342 position[X], position[Y], position[Z], -maxZ+0.1);
344 // Continue only if the mouse hit a cube, not the background.
345 if (position[Z] > (-maxZ + 0.1)) {
347 // Find which picture of a cube the mouse is on.
348 int cubeID = findWhichCube (sceneID, cubeViews, position);
349 if (cubeID < 0) {
350 // Could not find the nearest cube (should never happen).
351 return;
353 v = cubeViews.at (cubeID);
355 // Get the mouse position relative to the centre of the cube found.
356 // Use the transformation matrix for the fully translated and
357 // rotated view of that cube, including any user's rotations.
359 getGLPosition (mX, mY, depth, v->matrix, handle);
361 // Find the sticker at that (handle) position.
362 if (! cube->findSticker (handle, v->cubieSize, face1)) {
363 // Could not find the sticker (should never happen).
364 return;
367 // Find the axis that is NOT a possible axis of the slice move.
368 noTurn = cube->faceNormal (face1);
369 kDebug() << "FACE:" << face1[X] << face1[Y] << face1[Z];
371 foundHandle = true;
372 kDebug() << "Found handle:" << handle[0] << handle[1] << handle[2]
373 << mX << mY << "noTurn" << noTurn;
375 // Save the mouse-position for future tracking and comparison.
376 mX0 = mX; mY0 = mY;
377 mX1 = mX; mY1 = mY;
381 // After a button-release, perform the slice move required (if any).
382 if (event == ButtonUp) {
383 if (moveAngle != 0) {
384 // We found a move.
385 Move * move = new Move;
386 move->axis = currentMoveAxis;
387 move->slice = currentMoveSlice;
388 move->direction = currentMoveDirection;
390 kDebug() << "Append move:" << currentMoveAxis << currentMoveSlice
391 << currentMoveDirection; // IDW ***
392 emit newMove (move); // Signal the Game to store this move.
394 moveAngle = 0;
395 cube->setMoveAngle (0);
400 void MoveTracker::realignCube (QList<Move *> & tempMoves)
402 double fromAxis [nAxes * nAxes] = {1.0, 0.0, 0.0, // X axis.
403 0.0, 1.0, 0.0, // Y axis.
404 0.0, 0.0, 1.0}; // Z axis.
405 double toAxis [nAxes * nAxes] = {1.0, 0.0, 0.0, // X axis.
406 0.0, 1.0, 0.0, // Y axis.
407 0.0, 0.0, 1.0}; // Z axis.
409 printf ("\nTesting realignCube() ...\n"); // IDW
410 printf ("\nRotation state ");
411 rotationState.quaternionPrint();
413 // Find where the original X, Y and Z axes have ended up.
414 rotateAxes (rotationState, fromAxis, toAxis);
416 // Find which component of which axis is closest to X, Y, Z, -X, -Y or -Z.
417 int bestAligned = -1;
418 double component = 0.0;
419 LOOP (n, nAxes) {
420 int k = n * nAxes;
421 LOOP (m, nAxes) {
422 // Pick the largest component as the best aligned.
423 if (fabs(toAxis [k + m]) > component) {
424 bestAligned = k + m;
425 component = fabs(toAxis [k + m]);
429 printf ("\nBest aligned %d, component %6.3f, axis %d,%d\n",
430 bestAligned, toAxis[bestAligned], bestAligned/nAxes, bestAligned%nAxes);
432 // Determine which axis (X, -X, etc.) is closest to the best-aligned one.
433 int nAxisTo = bestAligned % nAxes;
435 double v1 [nAxes] = {0.0, 0.0, 0.0}; // The first axis to be aligned.
436 double v2 [nAxes] = {0.0, 0.0, 0.0}; // The axis to align it to.
438 int offset = bestAligned - nAxisTo;
439 LOOP (n, nAxes) {
440 v1 [n] = toAxis [offset + n];
443 // Determine whether the alignment will be parallel or anti-parallel.
444 v2 [nAxisTo] = toAxis [bestAligned] < 0.0 ? -1.0 : 1.0;
446 printf ("\n");
447 printf ("v1 (%6.3f %6.3f %6.3f)\n", v1[X], v1[Y], v1[Z]);
448 printf ("v2 (%6.3f %6.3f %6.3f)\n", v2[X], v2[Y], v2[Z]);
449 printf ("\n");
451 // Calculate the turn required to align the best-aligned axis exactly.
452 double turnVector [nAxes] = {1.0, 0.0, 0.0};
453 double turnAngle = 0.0;
454 if (getTurnVector (v1, v2, turnVector, turnAngle)) {
455 // Apply the required turn to the cube images (if non-zero).
456 rotationState.quaternionAddRotation (turnVector, turnAngle);
457 rotationState.quaternionPrint();
458 rotationState.quaternionToMatrix (rotationMatrix);
461 // Find where the original X, Y and Z axes are now.
462 rotateAxes (rotationState, fromAxis, toAxis);
464 // The remaining two axes must rotate in the plane perpendicular to the axis
465 // to which the first axis was aligned. Once one of the two axes is in
466 // place, the other axis will automatically follow.
468 double v3 [nAxes] = {0.0, 0.0, 0.0}; // The next axis to be aligned.
469 double v4 [nAxes] = {0.0, 0.0, 0.0}; // The axis with which to align.
471 // Pick the next axis to be aligned (in cyclical order).
472 int nAxis2 = ((bestAligned / nAxes) + 1) % nAxes;
473 int nAxis3 = (nAxis2 + 1) % nAxes;
474 printf ("\nAligned %d, yet to align %d and %d\n",
475 (bestAligned / nAxes), nAxis2, nAxis3);
477 v3 [nAxis2] = 1.0;
478 printf ("v3 (%6.3f %6.3f %6.3f)\n", v3[X], v3[Y], v3[Z]);
480 // Find where that axis is now, after the first alignment.
481 rotationState.quaternionRotateVector (v3);
482 printf ("Rotated (%6.3f, %6.3f, %6.3f)\n", v3 [X], v3 [Y], v3 [Z]);
484 // Find which axis is the closest axis one to align to.
485 component = 0.0;
486 nAxisTo = 0;
487 LOOP (n, nAxes) {
488 if (fabs (v3 [n]) > component) {
489 component = fabs (v3 [n]);
490 nAxisTo = n;
494 // Determine whether the alignment will be parallel or anti-parallel.
495 v4 [nAxisTo] = v3 [nAxisTo] < 0.0 ? -1.0 : 1.0;
497 printf ("\n");
498 printf ("v3 (%6.3f %6.3f %6.3f)\n", v3[X], v3[Y], v3[Z]);
499 printf ("v4 (%6.3f %6.3f %6.3f)\n", v4[X], v4[Y], v4[Z]);
500 printf ("\n");
502 // Calculate the turn required to align the two remaining axes exactly.
503 if (getTurnVector (v3, v4, turnVector, turnAngle)) {
504 // Apply the required turn to the cube images (if non-zero).
505 rotationState.quaternionAddRotation (turnVector, turnAngle);
506 rotationState.quaternionPrint();
507 rotationState.quaternionToMatrix (rotationMatrix);
510 // The original X, Y and Z axes should now be aligned, probably with other
511 // axes. So find out what is aligned with what and make the 90 or 180
512 // degree moves of the underlying cube needed to get to that position.
514 rotateAxes (rotationState, fromAxis, toAxis);
515 makeWholeCubeMoveList (tempMoves, toAxis);
517 // Finally set the cube images to the unrotated state.
518 rotationState.quaternionSetIdentity();
519 rotationState.quaternionToMatrix (rotationMatrix);
523 void MoveTracker::makeWholeCubeMoveList (QList<Move *> & tempMoves,
524 const double to [nAxes * nAxes])
526 // The cube has been rotated by the player and aligned to the standard
527 // orientation (top, front and right faces visible), however it will
528 // probably not be in its original orientation. This procedure works
529 // out a series of 90 and 180 degree moves that will get back there.
530 // The reverse of these moves is added to the player's list of moves
531 // and applied to the internal Cube object, so that the player's manual
532 // rotations are replaced by the equivalent 90 and 180 degree Cube moves.
534 // This makes keyboard and Singmaster-notation moves once again meaningful.
535 // For example, the Y-axis is again the one pointing up and T (top) again
536 // represents the top face of the cube, as seen by the player.
538 int fromI [nAxes] [nAxes] = {{1, 0, 0},
539 {0, 1, 0},
540 {0, 0, 1}};
541 int toI [nAxes] [nAxes] = {{0, 0, 0},
542 {0, 0, 0},
543 {0, 0, 0}};
544 bool notAligned = true;
545 int safetyLimit = 3;
547 LOOP (row, nAxes) {
548 LOOP (col, nAxes) {
549 if (fabs (to [row * nAxes + col]) > 0.999) {
550 toI [row] [col] = (to [row * nAxes + col] < 0.0) ? -1 : +1;
554 printf ("Old %2d %2d %2d %2d %2d %2d %2d %2d %2d\n",
555 fromI[0][0], fromI[0][1], fromI[0][2],
556 fromI[1][0], fromI[1][1], fromI[1][2],
557 fromI[2][0], fromI[2][1], fromI[2][2]);
558 printf ("New %2d %2d %2d %2d %2d %2d %2d %2d %2d\n",
559 toI[0][0], toI[0][1], toI[0][2],
560 toI[1][0], toI[1][1], toI[1][2],
561 toI[2][0], toI[2][1], toI[2][2]);
563 while (notAligned) {
564 if (--safetyLimit <= 0) notAligned = false;
565 // Determine whether any axes are fully aligned.
566 if ((toI [X][X] == 1) || (toI [Y][Y] == 1) || (toI [Z][Z] == 1)) {
567 // At least one axis is fully aligned.
568 if ((toI [X][X] == 1) && (toI [Y][Y] == 1) && (toI [Z][Z] == 1)) {
569 // All axes are fully aligned: time to stop.
570 notAligned = false;
571 break;
573 // Find which axis is fully aligned and what move aligns the others.
574 else {
575 int aligned = -1;
576 int reversed = -1;
577 LOOP (n, nAxes) {
578 if (toI [n][n] == -1) {
579 // This axis is reversed.
580 reversed = n;
582 if (toI [n][n] == 1) {
583 // This axis is fully aligned.
584 aligned = n;
587 if (aligned < 0) {
588 // Should never happen ...
590 // Calculate whether to rotate 90, -90 or 180 degrees.
591 Axis a = (Axis) aligned;
592 if (reversed >= 0) {
593 // One axis is aligned and both of the others are reversed.
594 // A single 180 degree move should bring all axes into line.
596 prepareWholeCubeMove (tempMoves, toI, a, ONE_EIGHTY);
598 else {
599 // A single 90 degree move should bring all axes into line.
601 // Calculate the required rotation around axis "a" from the
602 // positions of the other two axes (n1 and n2). For example
603 // if a is the Y axis, then n1 is the Z axis and n2 is the
604 // X axis (in cyclical order), so the Y axis (n1) is either
605 // pointing to +Z or -Z, as represented by (toI [Y][Z]).
607 Axis n1 = (Axis) ((a + 1) % nAxes);
608 Axis n2 = (Axis) ((a + 2) % nAxes);
609 prepareWholeCubeMove (tempMoves, toI, a,
610 (toI [n1][n2] > 0) ? CLOCKWISE : ANTICLOCKWISE);
614 // No axes are fully aligned: pick one to align.
615 else {
616 int reversed = -1;
617 LOOP (n, nAxes) {
618 if (toI [n][n] == -1) {
619 // This axis is reversed.
620 reversed = n;
621 break;
624 if (reversed >= 0) {
625 // Pick an axis that is not reversed and rotate it 180 degrees.
626 // That should bring one axis into line.
628 prepareWholeCubeMove (tempMoves, toI,
629 (Axis) ((reversed + 1) % nAxes), ONE_EIGHTY);
631 else {
632 // Pick any axis and rotate it 90 or -90 degrees to align it.
633 // If X is parallel/anti-parallel to the Z axis, we need to
634 // rotate around the Y axis and vice-versa.
635 Axis rAxis = (toI [X][Y] == 0) ? Y : Z;
636 Rotation r = CLOCKWISE;
637 r = (((rAxis == Y) && (toI [X][Z] > 0)) || // If X is at +Z
638 ((rAxis == Z) && (toI [X][Y] < 0))) ? // or -Y, turn
639 ANTICLOCKWISE : r; // anti-clock.
640 prepareWholeCubeMove (tempMoves, toI, rAxis, r);
647 void MoveTracker::prepareWholeCubeMove (QList<Move *> & moveList,
648 int to [nAxes][nAxes], const Axis a, const Rotation d)
650 int dir = (d == ANTICLOCKWISE) ? -1 : +1;
651 int count = (d == ONE_EIGHTY) ? 2 : 1;
653 Axis coord1 = (Axis) ((a + 1) % nAxes);
654 Axis coord2 = (Axis) ((a + 2) % nAxes);
655 int temp;
657 printf ("Prepare move: Axis %d direction %2d count %d\n", a, dir, count);
658 LOOP (n, count) {
659 LOOP (i, nAxes) {
660 temp = to [i][coord1];
661 to [i][coord1] = +dir * to [i][coord2];
662 to [i][coord2] = -dir * temp;
664 printf ("New %2d %2d %2d %2d %2d %2d %2d %2d %2d\n",
665 to[0][0], to[0][1], to[0][2],
666 to[1][0], to[1][1], to[1][2],
667 to[2][0], to[2][1], to[2][2]);
670 Move * move = new Move;
671 move->axis = a;
672 move->slice = WHOLE_CUBE;
673 move->direction = d;
674 move->degrees = 180;
676 if (d != ONE_EIGHTY) {
677 // Reverse a 90 degree move and set the angle to 90.
678 move->direction = (d == CLOCKWISE) ? ANTICLOCKWISE : CLOCKWISE;
679 move->degrees = 90;
682 // Stack the moves onto the list in reverse order.
683 moveList.prepend (move);
687 int MoveTracker::findWhichCube (const int sceneID,
688 const QList<CubeView *> cubeViews, const double position[])
690 // For some reason this function cannot compile with return-type CubeView *.
692 double distance = 10000.0; // Large value.
693 double d = 0.0;
694 double dx = 0.0;
695 double dy = 0.0;
696 double dz = 0.0;
697 int indexV = -1;
699 // Find which cube in the current scene is closest to the given position.
700 CubeView * v;
701 LOOP (n, cubeViews.size()) {
702 v = cubeViews.at (n);
703 if (v->sceneID != sceneID) {
704 continue; // Skip unwanted scene IDs.
707 dx = position[X] - v->position[X];
708 dy = position[Y] - v->position[Y];
709 dz = position[Z] - v->position[Z];
710 d = sqrt (dx*dx + dy*dy + dz*dz); // Pythagoras.
711 if (d < distance) {
712 distance = d;
713 indexV = n;
716 return (indexV);
720 void MoveTracker::rotateAxes (const Quaternion & r,
721 const double from [nAxes * nAxes], double to [nAxes * nAxes])
723 LOOP (n, nAxes) {
724 int k = n * nAxes;
725 printf ("Rotate vec (%6.3f, %6.3f, %6.3f)\n",
726 from [k + X], from [k + Y], from [k + Z]);
727 LOOP (m, nAxes) {
728 to [k + m] = from [k + m]; // Copy the input vector.
730 r.quaternionRotateVector (&(to [k])); // Rotate it.
731 printf ("Rotated %d (%6.3f, %6.3f, %6.3f)\n", n,
732 to [k + X], to [k + Y], to [k + Z]);
737 bool MoveTracker::getTurnVector (
738 const double v1 [nAxes], const double v2 [nAxes],
739 double turnAxis [nAxes], double & turnAngle)
741 double radius;
742 double u1 [nAxes], u2 [nAxes];
743 bool result = true;
745 // Make v1 and v2 into unit vectors.
746 radius = sqrt (v1[X]*v1[X] + v1[Y]*v1[Y] + v1[Z]*v1[Z]);
747 u1[X] = v1[X] / radius;
748 u1[Y] = v1[Y] / radius;
749 u1[Z] = v1[Z] / radius;
751 radius = sqrt (v2[X]*v2[X] + v2[Y]*v2[Y] + v2[Z]*v2[Z]);
752 u2[X] = v2[X] / radius;
753 u2[Y] = v2[Y] / radius;
754 u2[Z] = v2[Z] / radius;
756 // The vector is reversed for anti-clockwise rotations.
757 // The angle is positive, for both clockwise and anti-clockwise.
758 double rad = acos (u1[X]*u2[X] + u1[Y]*u2[Y] + u1[Z]*u2[Z]);
759 if (fabs (rad) >= 0.0001) {
760 // The angle is of reasonable size. It is safe to do the divisions.
761 double srad = sin (rad);
762 turnAxis[X] = -(u1[Y]*u2[Z] - u1[Z]*u2[Y]) / srad;
763 turnAxis[Y] = -(u1[Z]*u2[X] - u1[X]*u2[Z]) / srad;
764 turnAxis[Z] = -(u1[X]*u2[Y] - u1[Y]*u2[X]) / srad;
765 result = true; // Calculation succeeded.
767 else {
768 // The angle is less than 1 minute of arc. Avoid overflow on division.
769 rad = 0.0; // Zero angle (no rotation).
770 turnAxis[X] = 1.0; // Arbitrary axis (does not matter).
771 turnAxis[Y] = 0.0;
772 turnAxis[Z] = 0.0;
773 result = false; // Calculation failed.
775 turnAngle = rad * 180.0 / M_PI;
776 radius = sqrt (turnAxis[X]*turnAxis[X] +
777 turnAxis[Y]*turnAxis[Y] + turnAxis[Z]*turnAxis[Z]);
778 printf ("Turn Vector: radius %6.3f axis (%6.3f %6.3f %6.3f) angle %6.3f\n",
779 radius, turnAxis[X], turnAxis[Y], turnAxis[Z], turnAngle);
780 return result;
784 GLfloat MoveTracker::getMousePosition (const int mX, const int mY, double pos[])
786 GLfloat depth;
788 // Read the depth value at the position on the screen.
789 glReadPixels (mX, mY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
791 // Get the mouse position in OpenGL world co-ordinates.
792 getAbsGLPosition (mX, mY, depth, pos);
794 return depth;
798 void MoveTracker::getAbsGLPosition (int sX, int sY, GLfloat depth, double pos[])
800 GLdouble m [16];
801 glGetDoublev (GL_MODELVIEW_MATRIX, m);
802 getGLPosition (sX, sY, depth, m, pos);
806 void MoveTracker::getGLPosition (int sX, int sY, GLfloat depth,
807 double matrix[], double pos[])
809 // Retrieve the OpenGL projection matrix and viewport.
810 GLdouble p [16];
811 GLint v [4];
812 glGetDoublev (GL_PROJECTION_MATRIX, p);
813 glGetIntegerv (GL_VIEWPORT, v);
815 // Find the world coordinates of the nearest object at the screen position.
816 GLdouble objx, objy, objz;
817 GLint ret = gluUnProject (sX, sY, depth,
818 matrix, p, v,
819 &objx, &objy, &objz);
821 if (ret != GL_TRUE) {
822 kDebug() << "gluUnProject() did not succeed";
823 return;
826 // Return the OpenGL coordinates we found.
827 pos[X] = objx; pos[Y] = objy; pos[Z] = objz;
831 void MoveTracker::usersRotation()
833 glMultMatrixf (rotationMatrix);