https://24seconds.github.io/rubikscube/
This proeject is implemented only HTML(no canvas), CSS. javascript
. No Framework is used such as React, Vue or Angular.
Rubik's Cube is well-knwon puzzle. Therefore, there are some notations that helps you understand rubik's cube well.
I'm going to use notations (no worry! it's very simple notation!) which indicates the way we rotate Rubiks' cube and which orientation should we use.
- There are only
12
notations.(F, R, U, B, L, D, F', R', U', B', L', D')
- For example, let say, we rotate cube using
R
operation. That means, we rotateRight side
of cube inClockwise
. - Another example,
R'
, meansCounterclockwise
+Right side
rotation. Becasue there are6
faces in rubik's cube and2
orientations
For more information of notation, visit 'how-to-solve-a-rubix-cube` website and have a look notation part. (Korean website : 큐브를 맞추는 방법)
Imagine that you implement rubik's cube in html. How you implement cube's rotation? For me, I notice that rotation is actaully combination of two separate things.
- cube piece moves to other position.
- cube piece rotates according to its axis.
Here is an example about F
rotation.
The cube piece which has text 8 move to next position. While moving, cube piece rotate on the spot and it leads us to recognize that the cube is rotating correctly as we expect.
Therefore, at first, I tried to manage each cube piece state using translate3d
and rotate
like this. But there was problem...
addRotation(addedRotatoin) {
// ...after calculate process
this.element.style.transform = `
translate3d(${this.currentPosition.x}px,
${this.currentPosition.y}px,
${this.currentPosition.z}px)
rotateX(${newRotation.x}deg)
rotateY(${newRotation.y}deg)
otateZ(${newRotation.z}deg)`;
this.currentRotation = newRotation;
}
At first implementation, I thought rotation is just rotation. But there was a serious problem about XYZ-coordinate
rotation, named Gimbal lock.
When we naively rotate some object according to X, Y or Z axis in XYZ coordinate, in certain conditions, we can not rotate object as we want because of Gimbal lock.
I googled and it seems that there is no way to solve gimbal lock but to avoid gimbal lock. I googled again and using quaternion can solve this problem.
Quaternion is a number system and it is popular when we deal with 3D things. There are two concepts of expressing 3D rotation. One is matrix and the other one is quaternion.
For example, when we talk about 3D rotation, we usually manage rotation status as matrix. If we want to rotate theta degree about x axis, then we multiply current matrix by rotation matrix.
More Info, check wikipedia
Becasue using matrix can not avoid gimbal lock, quaternion is used. In quaternion, rotation status is expressed as like this
a + bi + cj + dk
a, b, c is real number and i, j, k is quaternion unit.
I'm not going to explain whole things. Check wikipedia for more information.
Because of gimbal lock, I managed rotation status using matrix3d
instead of rotate
.
addRotation(addedRotatoin) {
// ...after calculate process
this.element.style.transform =
`translate3d(${xP}px, ${yP}px, ${zP}px)
matrix3d(${r}, 0, 0, 0, 1)`;
this.currentQuaternion = newQuaternion;
}
To use quaternion, you need to able to convert XYZ rotation (called as euler rotation) to quaternion. Computation is quite complicated but no worry. There is good library to do this easily, named quaternion. Check this library if you want.
There are many ways to solve rubik's cube. In this project, to give a solution to user, Herbert Kociemba's two-phase algorithm is used. This algorithm gives at most 22 moves in half turn metric.
Luckily, there is javascript library which implements two-phase algorithm
, I used this library, named cubejs
.
When I start this project, I wanted to give appropriate user experience depend on their environment. So I decided to support mobile and desktop view both. Therefore, in Desktop, you can rotate Cube using either keyboard or mouse - in mouse, you can click rotation Buttons.
Also, to give better layout structure, CSS grid is used. You can check that operation stack
's position is changed depend on your browser width.
/* desktop view */
.cube-operation-stack-header {
grid-area: 1 / 2 / 1 / 2;
...other styles
}
ul.cube-operation-stack {
grid-column-start: 2;
grid-row: 2 / -1;
...other styles
}
/* mobile view */
@media only screen and (max-width: 720px) {
.cube-operation-stack-header {
display: none;
}
ul.cube-operation-stack {
grid-row: 4;
grid-column: 1 / -1;
...other styles
}
In this project, https://24seconds.github.io/rubikscube/, you can use either keyboard or mouse (mouse is for mobile web).
-
For keyboard, Click the screen and press one of
R, F, U, L, B, D
which is same as rubik's cube notations. To rotate incounterclockwise
, useshift button
. For example, R' is same as shift + R -
For mobile web view, instead of pressing keyboard,
just click buttons
. Operation mechanism is same as keyboard one.