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 *******************************************************************************/
22 // The RubikCube object uses the sqrt() function.
27 #include "gameglview.h"
31 Cube::Cube (QObject
* parent
, int xlen
, int ylen
, int zlen
)
38 // Generate a list of all the cubies in the cube.
39 // Set the centres of cubies at +ve and -ve co-ords around (0,0,0),
40 // as documented in "game.h". The cubie dimensions are 2x2x2. Each
41 // cubie's centre co-ordinate is (2*index - end-face-pos + 1).
44 while (! cubies
.isEmpty()) {
45 delete cubies
.takeFirst();
48 centre
[X
] = 2*i
- sizes
[X
] + 1;
50 centre
[Y
] = 2*j
- sizes
[Y
] + 1;
52 centre
[Z
] = 2*k
- sizes
[Z
] + 1;
53 cubies
.append (new Cubie (centre
));
58 addStickers (); // Add colored stickers to the faces.
61 moveInProgressAxis
= Z
; // Front face (+Z).
62 moveInProgressSlice
= sizes
[Z
] - 1;
63 moveInProgressAngle
= 0;
68 while (! cubies
.isEmpty()) {
69 delete cubies
.takeFirst();
73 void Cube::moveSlice (Axis axis
, int location
, Rotation direction
)
75 // If single-slice and not square, rotate 180 degrees rather than 90.
76 if ((location
!= WHOLE_CUBE
) &&
77 (sizes
[(axis
+ 1)%nAxes
] != sizes
[(axis
+ 2)%nAxes
])) {
78 direction
= ONE_EIGHTY
;
81 // Rotate all cubies that are in the required slice.
82 foreach (Cubie
* cubie
, cubies
) {
83 cubie
->rotate (axis
, location
, direction
);
88 void Cube::addStickers ()
90 int color
= INTERNAL
; // ie. Zero.
92 // Add stickers to cube faces in the order of axes X/Y/Z then -ve/+ve end.
95 int sign
= 2*minusPlus
- 1; // sign = -1 or +1.
96 int location
= sign
* sizes
[n
];
98 color
++; // FaceColor enum 1 --> 6.
99 foreach (Cubie
* cubie
, cubies
) {
100 cubie
->addSticker ((FaceColor
) color
, (Axis
) n
, location
, sign
);
107 void Cube::drawCube (GameGLView
* gameGLView
, float cubieSize
)
109 // For each cubie in the cube ...
110 foreach (Cubie
* cubie
, cubies
) {
112 if (cubie
->hasNoStickers()) {
113 // This cubie is deep inside the cube: save time by not drawing it.
117 // Draw the cubie and its stickers.
118 cubie
->drawCubie (gameGLView
, cubieSize
,
119 moveInProgressAxis
, moveInProgressSlice
, moveInProgressAngle
);
124 bool Cube::findSticker (double position
[], float myCubieSize
,
128 double location
[nAxes
];
129 double distance
= sqrt ((double) 2.0);
132 // Calculate the position in the cube's internal co-ordinate system.
134 location
[i
] = (position
[i
] / myCubieSize
) * 2.0;
135 // IDW faceCentre [i] = 0; // Return zeroes if no sticker is found.
138 foreach (Cubie
* cubie
, cubies
) {
139 d
= cubie
->findCloserSticker (distance
, location
, faceCentre
);
150 void Cube::setMoveInProgress (Axis axis
, int location
)
153 moveInProgressAxis
= axis
;
154 moveInProgressSlice
= location
;
158 void Cube::setMoveAngle (int angle
)
160 moveInProgressAngle
= angle
;
164 void Cube::setBlinkingOn (Axis axis
, int location
)
166 foreach (Cubie
* cubie
, cubies
) {
167 cubie
->setBlinkingOn (axis
, location
, sizes
[axis
]);
172 void Cube::setBlinkingOff ()
174 foreach (Cubie
* cubie
, cubies
) {
175 cubie
->setBlinkingOff ();
180 int Cube::faceNormal (int faceCentre
[3])
183 if (abs(faceCentre
[i
]) == sizes
[i
]) {
191 double Cube::convToOpenGL (int internalCoord
, double cubieSize
)
193 return ((double) internalCoord
/ 2.0) * cubieSize
;
196 Cubie::Cubie (int centre
[nAxes
])
199 originalCentre
[i
] = centre
[i
];
200 currentCentre
[i
] = centre
[i
];
207 while (! stickers
.isEmpty()) {
208 delete stickers
.takeFirst();
213 void Cubie::rotate (Axis axis
, int location
, Rotation direction
)
215 // Cubie moves only if it is in the required slice or in a whole-cube move.
216 if ((location
!= WHOLE_CUBE
) && (currentCentre
[axis
] != location
)) {
220 // The co-ordinate on the axis of rotation does not change, but we must
221 // work out what the other two co-ordinates are, in cyclical order: i.e.
222 // X-axis (0) --> Y,Z (co-ordinates 1 and 2 change),
223 // Y-axis (1) --> Z,X (co-ordinates 2 and 0 change),
224 // Z-axis (2) --> X,Y (co-ordinates 0 and 1 change).
226 Axis coord1
= (Axis
) ((axis
+ 1) % nAxes
);
227 Axis coord2
= (Axis
) ((axis
+ 2) % nAxes
);
231 case (ANTICLOCKWISE
): // eg. around the Z-axis, X --> Y and Y --> -X.
232 temp
= currentCentre
[coord1
];
233 currentCentre
[coord1
] = - currentCentre
[coord2
];
234 currentCentre
[coord2
] = + temp
;
235 foreach (Sticker
* s
, stickers
) {
236 temp
= s
->currentFaceCentre
[coord1
];
237 s
->currentFaceCentre
[coord1
] = - s
->currentFaceCentre
[coord2
];
238 s
->currentFaceCentre
[coord2
] = + temp
;
241 case (CLOCKWISE
): // eg. around the Z-axis, X --> -Y and Y --> X.
242 temp
= currentCentre
[coord1
];
243 currentCentre
[coord1
] = + currentCentre
[coord2
];
244 currentCentre
[coord2
] = - temp
;
245 foreach (Sticker
* s
, stickers
) {
246 temp
= s
->currentFaceCentre
[coord1
];
247 s
->currentFaceCentre
[coord1
] = + s
->currentFaceCentre
[coord2
];
248 s
->currentFaceCentre
[coord2
] = - temp
;
251 case (ONE_EIGHTY
): // eg. around the Z-axis, X --> -X and Y --> -Y.
252 currentCentre
[coord1
] = - currentCentre
[coord1
];
253 currentCentre
[coord2
] = - currentCentre
[coord2
];
254 foreach (Sticker
* s
, stickers
) {
255 s
->currentFaceCentre
[coord1
] = - s
->currentFaceCentre
[coord1
];
256 s
->currentFaceCentre
[coord2
] = - s
->currentFaceCentre
[coord2
];
265 void Cubie::addSticker (FaceColor color
, Axis axis
, int location
, int sign
)
267 // The cubie will get a sticker only if it is on the required face.
268 if (originalCentre
[axis
] != (location
- sign
)) {
273 Sticker
* s
= new Sticker
;
277 // The co-ordinates not on "axis" are the same as at the cubie's centre.
278 s
->originalFaceCentre
[n
] = originalCentre
[n
];
279 s
->currentFaceCentre
[n
] = originalCentre
[n
];
282 // The co-ordinate on "axis" is offset by -1 or +1 from the cubie's centre.
283 s
->originalFaceCentre
[axis
] = location
;
284 s
->currentFaceCentre
[axis
] = location
;
286 // Put the sticker on the cubie.
291 bool Cubie::hasNoStickers ()
293 return (stickers
.isEmpty ());
297 void Cubie::drawCubie (GameGLView
* gameGLView
, float cubieSize
,
298 Axis axis
, int slice
, int angle
)
300 float centre
[nAxes
];
302 // Calculate the centre of the cubie in OpenGL co-ordinates.
304 centre
[i
] = ((float) currentCentre
[i
]) * cubieSize
/ 2.0;
307 // If this cubie is in a moving slice, set its animation angle.
309 if ((angle
!= 0) && ((slice
== WHOLE_CUBE
) ||
310 (currentCentre
[axis
] == slice
))) {
314 // Draw this cubie in color zero (grey plastic color).
315 gameGLView
->drawACubie (cubieSize
, centre
, axis
, myAngle
);
317 float faceCentre
[nAxes
];
318 int faceNormal
[nAxes
];
320 // For each sticker on this cubie (there may be 0->3 stickers) ...
321 foreach (Sticker
* sticker
, stickers
) {
323 // Calculate the integer unit-vector normal to this sticker's face
324 // and the centre of the face, in floating OpenGL co-ordinates.
326 faceNormal
[j
] = sticker
->currentFaceCentre
[j
] - currentCentre
[j
];
327 faceCentre
[j
] = ((float) sticker
->currentFaceCentre
[j
]) *
331 // Draw this sticker in the required color, blink-intensity and size.
332 gameGLView
->drawASticker (cubieSize
, (int) sticker
->color
,
333 sticker
->blinking
, faceNormal
, faceCentre
);
336 // If cubie is moving, re-align the OpenGL axes with the rest of the cube.
338 gameGLView
->finishCubie ();
343 double Cubie::findCloserSticker (double distance
, double location
[],
347 double dmin
= distance
;
348 Sticker
* foundSticker
= 0;
350 foreach (Sticker
* sticker
, stickers
) {
353 len
= location
[n
] - sticker
->currentFaceCentre
[n
];
359 foundSticker
= sticker
;
363 if (foundSticker
!= 0) {
365 faceCentre
[n
] = foundSticker
->currentFaceCentre
[n
];
373 void Cubie::setBlinkingOn (Axis axis
, int location
, int cubeBoundary
)
375 // Exit if the cubie is not in the slice that is going to move.
376 if ((location
!= WHOLE_CUBE
) && (currentCentre
[axis
] != location
)) {
380 // If the sticker is on the outside edges of the slice, make it blink, but
381 // not if it is perpendicular to the move-axis (ie. on the slice's face).
382 foreach (Sticker
* sticker
, stickers
) {
383 if (abs(sticker
->currentFaceCentre
[axis
]) != cubeBoundary
) {
384 sticker
->blinking
= true;
390 void Cubie::setBlinkingOff ()
392 foreach (Sticker
* sticker
, stickers
) {
393 sticker
->blinking
= false;
398 void Cubie::printAll ()
400 printf ("%2d %2d %2d -> %2d %2d %2d Stickers: ",
401 originalCentre
[X
], originalCentre
[Y
], originalCentre
[Z
],
402 currentCentre
[X
], currentCentre
[Y
], currentCentre
[Z
]);
404 if (stickers
.isEmpty ()) {
408 foreach (Sticker
* sticker
, stickers
) {
409 printf ("<%d> at ", (int) sticker
->color
);
411 printf ("%2d ", sticker
->currentFaceCentre
[n
]);
419 void Cubie::printChanges ()
423 // Check if the cubie's centre is in a new position.
425 if (currentCentre
[i
] != originalCentre
[i
])
429 // Check if the cubie is back where it was but has been given a twist.
431 foreach (Sticker
* s
, stickers
) {
433 if (s
->currentFaceCentre
[i
] != s
->originalFaceCentre
[i
])
439 // If anything has changed, print the cubie.