File indexing completed on 2024-04-14 04:02:09

0001 /*
0002     SPDX-FileCopyrightText: 2008 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef CUBE_H
0008 #define CUBE_H
0009 
0010 #include <QObject>
0011 
0012 #include "kbkglobal.h"
0013 
0014 class GameGLView;
0015 
0016 class Cubie;        // Forward declaration of Cube's component.
0017 
0018 // Face-sticker colors are in the order of axes X/Y/Z then -ve/+ve direction.
0019 // INTERNAL is the color of the material of which the cube is made (eg. gray).
0020 enum    FaceColor   {INTERNAL, LEFT, RIGHT, BOTTOM, TOP, BACK, FRONT};
0021 
0022 /** 
0023  * The Cube class represents a Rubik's Cube in an abstract way.
0024  *
0025  * Actually the "cube" is a rectangular parallelepiped, which can have unequal
0026  * sides, like a brick, or can be one layer thick, like a mat.  The cube is
0027  * made up of many "cubies" (small cubes) stacked in a 3-D array.  The original
0028  * Rubik's Cube was an array of 3x3x3 cubies, but in this version the sides can
0029  * have any number of cubies between 1 and 6, but only one side can be 1 cubie
0030  * long, otherwise the puzzle becomes too easy (e.g. the dimensions can be
0031  * 1x2x2, 6x6x6, 4x1x3, 3x4x5, etc.).
0032  *
0033  * The six faces of the cube are covered in stickers of six different colors.  
0034  * Cubies are dark grey, but can have stickers on one, two or three of their
0035  * six faces, depending on whether they are located in the middle of a cube
0036  * face, on a cube edge or at a cube corner.  If all cube dimensions are 3 or
0037  * more, some cubies will be hidden and will have no stickers (e.g. a 3x3x3
0038  * cube has one hidden cubie).  To save time when handling large cubes, we do
0039  * not draw hidden cubies.
0040  *
0041  * The cube can be "shuffled" by rotating "slices" of cubies that lie in the
0042  * same plane, analogous to slices of a loaf of bread.  The faces of the cube
0043  * then become a jumble of colors.
0044  *
0045  * The object of the game is to "solve" a shuffled cube, by selecting and
0046  * rotating slices in sequence so that all the cube faces end up with their
0047  * original colors.
0048  *
0049  * This class holds the current position of the cube, using an abstract set
0050  * of co-ordinates that simplify procedures such as creating, painting and
0051  * moving a cube.  Firstly, all cubies are of size 2x2x2.  This means that
0052  * the centre of a cubie always has integer co-ordinates, regardless of
0053  * whether the number of cubies in a side is even or odd.
0054  *
0055  * The origin of the co-ordinates is at the centre of gravity of the cube and
0056  * the centres of the cubies have positive and negative co-ordinates running
0057  * from there.  Looking at one co-ordinate only of a line of cubies, we have:
0058  *
0059  *     Centres of 3 cubies:   |-2 | 0 | +2|   - End faces at -3 and +3
0060  *
0061  *     Centres of 4 cubies: |-3 |-1 | +1| +3| - End faces at -4 and +4
0062  *
0063  * Note that the origin (zero) is between two cubies when the number of cubies
0064  * in the line is even.  More importantly, note that the end faces of the line,
0065  * which will have stickers, are always at -N and +N, where N is the number
0066  * of cubies in the line.  In a full cube, consisting of LxMxN cubies, the six
0067  * colored faces are at distances -L, +L, -M, +M, -N and +N from the origin.
0068  *
0069  * A slice (see above) is represented as all cubies whose centres have the same
0070  * value in one of the co-ordinates.  That value also gives the location of the
0071  * slice and the axis around which to rotate the slice.  For example, in a
0072  * 3x3x3 cube, the slice containing the right-hand face will consist of all
0073  * cubies whose centres have X-coordinate = +2 and the axis around which the
0074  * slice rotates is the X-axis itself, which runs from left to right through
0075  * the centre of the cube (the origin of co-ordinates).
0076  *
0077  * Finally, looking at the screen, the X-axis runs from left to right, the
0078  * Y-axis runs from bottom to top (as in mathematics) and the Z-axis runs from
0079  * back to front (out of the screen towards you), which is as in OpenGL library
0080  * usage.  Last but not least, the co-ordinates of points such as the centre of
0081  * a cubie are stored in arrays of size 3 (e.g. the X, Y and Z co-ordinates of
0082  * a point P are in array elements P[0], P[1] and P[3] and the X, Y and Z axes
0083  * are now the "0", "1" and "2" axes.
0084  *
0085  * This is handy because just two numbers, an axis number and a co-ordinate
0086  * value can represent any slice or face of the cube.  For example the top and
0087  * bottom faces of a 3x3x3 cube are (1,3) and (1,-3) and the central horizontal
0088  * slice is (1,0), representing the planes Y = +3 and Y = -3 and the 9 cubies
0089  * whose centres have Y = 0.
0090  */
0091 class Cube : public QObject
0092 {
0093 public:
0094     /**
0095      * Constructor for the Cube object
0096      * @param parent    The parent widget
0097      * @param xlen  The number of cubies in the X direction (left to right)
0098      * @param ylen  The number of cubies in the Y direction (bottom to top)
0099      * @param zlen  The number of cubies in the Z direction (back to front)
0100      */
0101     explicit Cube (QObject * parent = nullptr, int xlen = 3, int ylen = 3, int zlen = 3);
0102     ~Cube() override;
0103 
0104     void drawCube    (GameGLView * gameGLView, float cubieSize);
0105     void moveSlice   (Axis axis, int location, Rotation direction);
0106 
0107     void setMoveInProgress (Axis axis, int location);
0108     void setMoveAngle      (int angle);
0109     void setBlinkingOn     (Axis axis, int location);
0110     void setBlinkingOff    ();
0111 
0112     bool findSticker (double position [nAxes], float myCubieSize,
0113                 int faceCentre [nAxes]);
0114     int  faceNormal        (int faceCentre [3]);
0115     double convToOpenGL    (int internalCoord, double cubieSize);
0116 
0117 private:
0118     void addStickers ();        // Add colored stickers to the faces.
0119 
0120     int   sizes [nAxes];        // The number of cubies on each axis.
0121     QList<Cubie *>  cubies;     // The list of cubies in the cube.
0122 
0123     Axis  moveInProgressAxis;
0124     int   moveInProgressSlice;
0125     int   moveInProgressAngle;
0126 };
0127 
0128 class Cubie : public QObject
0129 {
0130 public:
0131     /**
0132      * Constructor for the Cubie object
0133      * @param centre    The co-ordinates of the central point (int [nAxes])
0134      */
0135     explicit Cubie (int centre [nAxes]);
0136     ~Cubie () override;
0137 
0138     void rotate (Axis axis, int location, Rotation direction);
0139 
0140     void addSticker (FaceColor color, Axis axis, int location, int sign);
0141 
0142     bool hasNoStickers ();
0143 
0144     void drawCubie (GameGLView * gameGLView, float cubieSize,
0145                 Axis axis, int slice, int angle);
0146 
0147     double findCloserSticker (double distance, double location [],
0148                   int faceCentre []);
0149     void setBlinkingOn  (Axis axis, int location, int cubeBoundary);
0150     void setBlinkingOff ();
0151 
0152     void printAll ();
0153     void printChanges ();
0154 
0155 private:
0156     int originalCentre [nAxes];     // Original location of the cubie.
0157     int currentCentre  [nAxes];     // Current location of the cubie.
0158 
0159     typedef struct {            // Define type "Sticker".
0160     FaceColor color;
0161     bool      blinking;
0162     int originalFaceCentre [nAxes];
0163     int currentFaceCentre  [nAxes];
0164     } Sticker;
0165 
0166     QList<Sticker *>    stickers;   // The stickers on the cubie (if any).
0167 };
0168 
0169 #endif  // CUBE_H