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"
27 #include <KMessageBox>
29 MoveTracker::MoveTracker (QWidget
* parent
)
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
) {
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
)
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.
93 double axis
[nAxes
] = {1.0, 0.0, 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.
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
);
124 // Could not find the nearest cube (should never happen).
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
] +
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
)
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.
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
);
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
;
189 printf ("a = %7.2f, b = %7.2f, c = %7.2f, b^2 - 4ac = %7.2f\n",
194 lambda
= (-b
- sqrt (q
)) / (2.0*a
);
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.
204 printf ("The line of sight is outside the handle-sphere.\n");
209 printf ("Lambda: %9.4f\n", lambda
);
212 // Set up a vector for the old position on the handle-sphere.
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
);
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
]);
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
))) {
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
))
255 if ((moveAngle
== 0) && outsideHandleArea
) {
256 // Mouse just moved outside handle area: find the required move.
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
]);
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
);
293 double d
= position
[n
] - handle
[n
];
294 kDebug() << "Axis:" << n
<< "distance" << d
;
295 if (fabs (d
) > fabs (distance
)) {
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
) {
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.
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
);
350 // Could not find the nearest cube (should never happen).
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).
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
];
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.
381 // After a button-release, perform the slice move required (if any).
382 if (event
== ButtonUp
) {
383 if (moveAngle
!= 0) {
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.
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;
422 // Pick the largest component as the best aligned.
423 if (fabs(toAxis
[k
+ m
]) > component
) {
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
;
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;
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
]);
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
);
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.
488 if (fabs (v3
[n
]) > component
) {
489 component
= fabs (v3
[n
]);
494 // Determine whether the alignment will be parallel or anti-parallel.
495 v4
[nAxisTo
] = v3
[nAxisTo
] < 0.0 ? -1.0 : 1.0;
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
]);
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},
541 int toI
[nAxes
] [nAxes
] = {{0, 0, 0},
544 bool notAligned
= true;
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]);
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.
573 // Find which axis is fully aligned and what move aligns the others.
578 if (toI
[n
][n
] == -1) {
579 // This axis is reversed.
582 if (toI
[n
][n
] == 1) {
583 // This axis is fully aligned.
588 // Should never happen ...
590 // Calculate whether to rotate 90, -90 or 180 degrees.
591 Axis a
= (Axis
) aligned
;
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
);
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.
618 if (toI
[n
][n
] == -1) {
619 // This axis is reversed.
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
);
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
);
657 printf ("Prepare move: Axis %d direction %2d count %d\n", a
, dir
, count
);
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
;
672 move
->slice
= WHOLE_CUBE
;
676 if (d
!= ONE_EIGHTY
) {
677 // Reverse a 90 degree move and set the angle to 90.
678 move
->direction
= (d
== CLOCKWISE
) ? ANTICLOCKWISE
: CLOCKWISE
;
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.
699 // Find which cube in the current scene is closest to the given position.
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.
720 void MoveTracker::rotateAxes (const Quaternion
& r
,
721 const double from
[nAxes
* nAxes
], double to
[nAxes
* nAxes
])
725 printf ("Rotate vec (%6.3f, %6.3f, %6.3f)\n",
726 from
[k
+ X
], from
[k
+ Y
], from
[k
+ Z
]);
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
)
742 double u1
[nAxes
], u2
[nAxes
];
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.
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).
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
);
784 GLfloat
MoveTracker::getMousePosition (const int mX
, const int mY
, double pos
[])
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
);
798 void MoveTracker::getAbsGLPosition (int sX
, int sY
, GLfloat depth
, double pos
[])
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.
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
,
819 &objx
, &objy
, &objz
);
821 if (ret
!= GL_TRUE
) {
822 kDebug() << "gluUnProject() did not succeed";
826 // Return the OpenGL coordinates we found.
827 pos
[X
] = objx
; pos
[Y
] = objy
; pos
[Z
] = objz
;
831 void MoveTracker::usersRotation()
833 glMultMatrixf (rotationMatrix
);