@houstonp/rubiks-cube 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -77
- package/package.json +18 -8
- package/src/{core.js → core/index.js} +72 -41
- package/src/rubiksCube/index.js +3 -0
- package/src/rubiksCube/rubiksCubeController.js +111 -0
- package/src/{three → rubiksCube3D}/centerPiece.js +37 -2
- package/src/{three → rubiksCube3D}/cornerPiece.js +56 -2
- package/src/rubiksCube3D/cubeConfig.js +87 -0
- package/src/rubiksCube3D/cubeSettings.js +30 -0
- package/src/{three → rubiksCube3D}/edgePiece.js +2 -1
- package/src/rubiksCube3D/index.js +3 -0
- package/src/rubiksCube3D/rubiksCube3D.js +383 -0
- package/src/{three → rubiksCube3D}/sticker.js +5 -4
- package/src/state/index.js +4 -0
- package/src/state/rubiksCubeState.js +471 -0
- package/src/state/slice.js +236 -0
- package/src/state/stickerState.js +185 -0
- package/src/{camera → webComponent}/cameraState.js +17 -25
- package/src/webComponent/constants.js +67 -0
- package/src/webComponent/index.js +7 -0
- package/src/webComponent/rubiksCubeElement.js +379 -0
- package/src/{settings.js → webComponent/settings.js} +36 -23
- package/tests/common.js +3 -20
- package/tests/core.test.js +56 -0
- package/tests/rubiksCube.solves.test.js +41 -0
- package/tests/rubiksCube3D.solves.test.js +185 -0
- package/tests/rubiksCubeState.solves.test.js +35 -0
- package/tests/testScrambles.js +194 -0
- package/types/{core.d.ts → core/index.d.ts} +45 -48
- package/types/rubiksCube/index.d.ts +3 -0
- package/types/rubiksCube/rubiksCubeController.d.ts +62 -0
- package/types/rubiksCube3D/centerPiece.d.ts +27 -0
- package/types/rubiksCube3D/cornerPiece.d.ts +38 -0
- package/types/rubiksCube3D/cubeConfig.d.ts +32 -0
- package/types/rubiksCube3D/cubeSettings.d.ts +33 -0
- package/types/{three → rubiksCube3D}/edgePiece.d.ts +5 -3
- package/types/rubiksCube3D/index.d.ts +3 -0
- package/types/rubiksCube3D/rubiksCube3D.d.ts +120 -0
- package/types/rubiksCube3D/sticker.d.ts +18 -0
- package/types/state/index.d.ts +5 -0
- package/types/state/rubiksCubeState.d.ts +108 -0
- package/types/state/slice.d.ts +46 -0
- package/types/state/stickerState.d.ts +34 -0
- package/types/webComponent/cameraState.d.ts +22 -0
- package/types/webComponent/constants.d.ts +57 -0
- package/types/webComponent/index.d.ts +6 -0
- package/types/webComponent/rubiksCubeElement.d.ts +89 -0
- package/types/{settings.d.ts → webComponent/settings.d.ts} +5 -8
- package/src/cube/animationSlice.js +0 -205
- package/src/cube/animationState.js +0 -96
- package/src/cube/cubeSettings.js +0 -19
- package/src/cube/cubeState.js +0 -337
- package/src/cube/stickerState.js +0 -188
- package/src/index.js +0 -621
- package/src/three/cube.js +0 -492
- package/tests/cube.five.test.js +0 -126
- package/tests/cube.four.test.js +0 -126
- package/tests/cube.seven.test.js +0 -126
- package/tests/cube.six.test.js +0 -126
- package/tests/cube.three.test.js +0 -151
- package/tests/cube.two.test.js +0 -125
- package/types/camera/cameraState.d.ts +0 -19
- package/types/cube/animationSlice.d.ts +0 -26
- package/types/cube/animationState.d.ts +0 -41
- package/types/cube/cubeSettings.d.ts +0 -17
- package/types/cube/cubeState.d.ts +0 -47
- package/types/cube/stickerState.d.ts +0 -21
- package/types/index.d.ts +0 -87
- package/types/three/centerPiece.d.ts +0 -15
- package/types/three/cornerPiece.d.ts +0 -24
- package/types/three/cube.d.ts +0 -130
- package/types/three/sticker.d.ts +0 -15
- /package/src/{debouncer.js → webComponent/debouncer.js} +0 -0
- /package/src/{globals.ts → webComponent/globals.ts} +0 -0
- /package/types/{debouncer.d.ts → webComponent/debouncer.d.ts} +0 -0
- /package/types/{globals.d.ts → webComponent/globals.d.ts} +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { CubeTypes, Faces } from '../core';
|
|
3
|
+
/** @import {Face, CubeType} from '../core' */
|
|
4
|
+
/** @import {vector} from './rubiksCubeState' */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef StickerState
|
|
8
|
+
* @property {Face[][]} Face.U
|
|
9
|
+
* @property {Face[][]} Face.D
|
|
10
|
+
* @property {Face[][]} Face.F
|
|
11
|
+
* @property {Face[][]} Face.B
|
|
12
|
+
* @property {Face[][]} Face.L
|
|
13
|
+
* @property {Face[][]} Face.R
|
|
14
|
+
**/
|
|
15
|
+
|
|
16
|
+
const ERROR_MARGIN = 0.0001;
|
|
17
|
+
const LayerCount = {
|
|
18
|
+
[CubeTypes.Two]: 2,
|
|
19
|
+
[CubeTypes.Three]: 3,
|
|
20
|
+
[CubeTypes.Four]: 4,
|
|
21
|
+
[CubeTypes.Five]: 5,
|
|
22
|
+
[CubeTypes.Six]: 6,
|
|
23
|
+
[CubeTypes.Seven]: 7,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {vector} stickerDirection
|
|
28
|
+
* @param {vector} piecePosition
|
|
29
|
+
* @param {number[]} layers
|
|
30
|
+
* @returns {{face: Face, i: number, j: number}}
|
|
31
|
+
*/
|
|
32
|
+
export function getStickerFaceIndex(stickerDirection, piecePosition, layers) {
|
|
33
|
+
const last = layers.length - 1;
|
|
34
|
+
/** @type {(val: number) => number} */
|
|
35
|
+
const layerIndex = (val) => {
|
|
36
|
+
for (let i = 0; i < layers.length; i++) {
|
|
37
|
+
if (Math.abs(val - layers[i]) < ERROR_MARGIN) {
|
|
38
|
+
return i;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Failed to get layer number. position ${val} not found in layers ${layers}`);
|
|
42
|
+
};
|
|
43
|
+
if (stickerDirection.x === 1) return { face: Faces.R, i: last - layerIndex(piecePosition.y), j: last - layerIndex(piecePosition.z) };
|
|
44
|
+
if (stickerDirection.x === -1) return { face: Faces.L, i: last - layerIndex(piecePosition.y), j: layerIndex(piecePosition.z) };
|
|
45
|
+
if (stickerDirection.y === 1) return { face: Faces.U, i: layerIndex(piecePosition.z), j: layerIndex(piecePosition.x) };
|
|
46
|
+
if (stickerDirection.y === -1) return { face: Faces.D, i: last - layerIndex(piecePosition.z), j: layerIndex(piecePosition.x) };
|
|
47
|
+
if (stickerDirection.z === 1) return { face: Faces.F, i: last - layerIndex(piecePosition.y), j: layerIndex(piecePosition.x) };
|
|
48
|
+
if (stickerDirection.z === -1) return { face: Faces.B, i: last - layerIndex(piecePosition.y), j: last - layerIndex(piecePosition.x) };
|
|
49
|
+
throw new Error(`StickerDirection is not a standard unit vector. vector: ${stickerDirection}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @param {CubeType} cubeType
|
|
55
|
+
* @return {StickerState}
|
|
56
|
+
*/
|
|
57
|
+
export const defaultStickerState = (cubeType) => {
|
|
58
|
+
const n = LayerCount[cubeType];
|
|
59
|
+
if (n == null) {
|
|
60
|
+
throw new Error(`Invalid CubeType`);
|
|
61
|
+
}
|
|
62
|
+
return initialStickerState(n);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @param {number} layerCount
|
|
68
|
+
* @returns {StickerState}
|
|
69
|
+
*/
|
|
70
|
+
const initialStickerState = (layerCount) => {
|
|
71
|
+
const state = {
|
|
72
|
+
[Faces.R]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.R)),
|
|
73
|
+
[Faces.U]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.U)),
|
|
74
|
+
[Faces.F]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.F)),
|
|
75
|
+
[Faces.B]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.B)),
|
|
76
|
+
[Faces.D]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.D)),
|
|
77
|
+
[Faces.L]: Array.from({ length: layerCount }, () => Array.from({ length: layerCount }, () => Faces.L)),
|
|
78
|
+
};
|
|
79
|
+
return state;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* @param {CubeType} cubeType
|
|
85
|
+
* @return {StickerState}
|
|
86
|
+
*/
|
|
87
|
+
export const getEmptyStickerState = (cubeType) => {
|
|
88
|
+
const n = LayerCount[cubeType];
|
|
89
|
+
if (n == null) {
|
|
90
|
+
throw new Error(`Invalid CubeType`);
|
|
91
|
+
}
|
|
92
|
+
return emptyStickerState(n);
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
*
|
|
96
|
+
* @param {number} layerCount
|
|
97
|
+
* @returns {StickerState}
|
|
98
|
+
*/
|
|
99
|
+
const emptyStickerState = (layerCount) => {
|
|
100
|
+
const state = {
|
|
101
|
+
[Faces.R]: Array.from({ length: layerCount }, () => []),
|
|
102
|
+
[Faces.U]: Array.from({ length: layerCount }, () => []),
|
|
103
|
+
[Faces.F]: Array.from({ length: layerCount }, () => []),
|
|
104
|
+
[Faces.B]: Array.from({ length: layerCount }, () => []),
|
|
105
|
+
[Faces.D]: Array.from({ length: layerCount }, () => []),
|
|
106
|
+
[Faces.L]: Array.from({ length: layerCount }, () => []),
|
|
107
|
+
};
|
|
108
|
+
return state;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {StickerState} stickerState
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
export function toKociemba(stickerState) {
|
|
116
|
+
return `${stickerState.U.flat().join('')}${stickerState.R.flat().join('')}${stickerState.F.flat().join('')}${stickerState.D.flat().join('')}${stickerState.L.flat().join('')}${stickerState.B.flat().join('')}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {string} kociembaString
|
|
121
|
+
* @returns {StickerState | undefined} stickerState
|
|
122
|
+
*/
|
|
123
|
+
export function fromKociemba(kociembaString) {
|
|
124
|
+
switch (kociembaString.length) {
|
|
125
|
+
case 6 * 2 * 2:
|
|
126
|
+
return fromKociembaWithLayerCount(kociembaString, 2);
|
|
127
|
+
case 6 * 3 * 3:
|
|
128
|
+
return fromKociembaWithLayerCount(kociembaString, 3);
|
|
129
|
+
case 6 * 4 * 4:
|
|
130
|
+
return fromKociembaWithLayerCount(kociembaString, 4);
|
|
131
|
+
case 6 * 5 * 5:
|
|
132
|
+
return fromKociembaWithLayerCount(kociembaString, 5);
|
|
133
|
+
case 6 * 6 * 6:
|
|
134
|
+
return fromKociembaWithLayerCount(kociembaString, 6);
|
|
135
|
+
case 6 * 7 * 7:
|
|
136
|
+
return fromKociembaWithLayerCount(kociembaString, 7);
|
|
137
|
+
default:
|
|
138
|
+
console.error(`Invalid state string length.`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} kociembaString
|
|
145
|
+
* @param {number} layerCount
|
|
146
|
+
* @returns {StickerState | undefined } stickerState
|
|
147
|
+
*/
|
|
148
|
+
function fromKociembaWithLayerCount(kociembaString, layerCount) {
|
|
149
|
+
let stickerState = emptyStickerState(layerCount);
|
|
150
|
+
for (let i = 0; i < 6; i++) {
|
|
151
|
+
const faceString = kociembaString.slice(i * layerCount ** 2, (i + 1) * layerCount ** 2);
|
|
152
|
+
for (let j = 0; j < layerCount; j++) {
|
|
153
|
+
const rowString = faceString.slice(j * layerCount, (j + 1) * layerCount);
|
|
154
|
+
for (let k = 0; k < layerCount; k++) {
|
|
155
|
+
const face = Object.values(Faces).find((face) => rowString[k] === face);
|
|
156
|
+
if (!face) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
switch (i) {
|
|
160
|
+
case 0:
|
|
161
|
+
stickerState.U[j][k] = face;
|
|
162
|
+
break;
|
|
163
|
+
case 1:
|
|
164
|
+
stickerState.R[j][k] = face;
|
|
165
|
+
break;
|
|
166
|
+
case 2:
|
|
167
|
+
stickerState.F[j][k] = face;
|
|
168
|
+
break;
|
|
169
|
+
case 3:
|
|
170
|
+
stickerState.D[j][k] = face;
|
|
171
|
+
break;
|
|
172
|
+
case 4:
|
|
173
|
+
stickerState.L[j][k] = face;
|
|
174
|
+
break;
|
|
175
|
+
case 5:
|
|
176
|
+
stickerState.B[j][k] = face;
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
throw new Error(`Invalid value for i - [${i}]. i should be between [0,5] inclusive.`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return stickerState;
|
|
185
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import {
|
|
2
|
+
import { PeekActions, PeekStates } from './constants';
|
|
3
|
+
/** @import {PeekAction, PeekState} from './constants' */
|
|
3
4
|
|
|
4
5
|
export class CameraState {
|
|
5
6
|
/**
|
|
@@ -14,51 +15,48 @@ export class CameraState {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
* @param {
|
|
18
|
+
* @param {PeekAction} action
|
|
18
19
|
*/
|
|
19
|
-
peekCamera(
|
|
20
|
-
switch (
|
|
21
|
-
case
|
|
20
|
+
peekCamera(action) {
|
|
21
|
+
switch (action) {
|
|
22
|
+
case PeekActions.Horizontal:
|
|
22
23
|
this.Right = !this.Right;
|
|
23
24
|
break;
|
|
24
|
-
case
|
|
25
|
+
case PeekActions.Vertical:
|
|
25
26
|
this.Up = !this.Up;
|
|
26
27
|
break;
|
|
27
|
-
case
|
|
28
|
+
case PeekActions.Right:
|
|
28
29
|
this.Right = true;
|
|
29
30
|
break;
|
|
30
|
-
case
|
|
31
|
+
case PeekActions.Left:
|
|
31
32
|
this.Right = false;
|
|
32
33
|
break;
|
|
33
|
-
case
|
|
34
|
+
case PeekActions.Up:
|
|
34
35
|
this.Up = true;
|
|
35
36
|
break;
|
|
36
|
-
case
|
|
37
|
+
case PeekActions.Down:
|
|
37
38
|
this.Up = false;
|
|
38
39
|
break;
|
|
39
|
-
case
|
|
40
|
+
case PeekActions.RightUp:
|
|
40
41
|
this.Right = true;
|
|
41
42
|
this.Up = true;
|
|
42
43
|
break;
|
|
43
|
-
case
|
|
44
|
+
case PeekActions.RightDown:
|
|
44
45
|
this.Right = true;
|
|
45
46
|
this.Up = false;
|
|
46
47
|
break;
|
|
47
|
-
case
|
|
48
|
+
case PeekActions.LeftUp:
|
|
48
49
|
this.Right = false;
|
|
49
50
|
this.Up = true;
|
|
50
51
|
break;
|
|
51
|
-
case
|
|
52
|
+
case PeekActions.LeftDown:
|
|
52
53
|
this.Right = false;
|
|
53
54
|
this.Up = false;
|
|
54
55
|
break;
|
|
55
|
-
default:
|
|
56
|
-
console.error(`Invalid peekType:[${peekType}]. valid values are [${Object.values(PeekTypes)}] `);
|
|
57
|
-
break;
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
/**
|
|
61
|
-
* @returns {
|
|
59
|
+
* @returns {PeekState}
|
|
62
60
|
*/
|
|
63
61
|
toPeekState() {
|
|
64
62
|
if (this.Right && this.Up) {
|
|
@@ -70,12 +68,6 @@ export class CameraState {
|
|
|
70
68
|
if (this.Right && !this.Up) {
|
|
71
69
|
return PeekStates.RightDown;
|
|
72
70
|
}
|
|
73
|
-
|
|
74
|
-
return PeekStates.LeftDown;
|
|
75
|
-
}
|
|
76
|
-
console.error(
|
|
77
|
-
`Invalid CameraState right and up values must be true or false. Actual values: right[${this.Right}] up[${this.Up}]. Default RightUp returned`,
|
|
78
|
-
);
|
|
79
|
-
return PeekStates.RightUp;
|
|
71
|
+
return PeekStates.LeftDown;
|
|
80
72
|
}
|
|
81
73
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef CameraOptions
|
|
3
|
+
* @property {number} [cameraSpeedMs]
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {typeof AnimationStyles[keyof typeof AnimationStyles]} AnimationStyle
|
|
8
|
+
*/
|
|
9
|
+
export const AnimationStyles = Object.freeze({
|
|
10
|
+
Exponential: 'exponential',
|
|
11
|
+
Linear: 'linear',
|
|
12
|
+
Next: 'next',
|
|
13
|
+
Fixed: 'fixed',
|
|
14
|
+
Match: 'match',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {typeof PeekStates [keyof typeof PeekStates]} PeekState
|
|
19
|
+
*/
|
|
20
|
+
export const PeekStates = Object.freeze({
|
|
21
|
+
RightUp: 'rightUp',
|
|
22
|
+
RightDown: 'rightDown',
|
|
23
|
+
LeftUp: 'leftUp',
|
|
24
|
+
LeftDown: 'leftDown',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {typeof PeekActions [keyof typeof PeekActions]} PeekAction
|
|
29
|
+
*/
|
|
30
|
+
export const PeekActions = Object.freeze({
|
|
31
|
+
Horizontal: 'horizontal',
|
|
32
|
+
Vertical: 'vertical',
|
|
33
|
+
Right: 'right',
|
|
34
|
+
Left: 'left',
|
|
35
|
+
Up: 'up',
|
|
36
|
+
Down: 'down',
|
|
37
|
+
RightUp: 'rightUp',
|
|
38
|
+
RightDown: 'rightDown',
|
|
39
|
+
LeftUp: 'leftUp',
|
|
40
|
+
LeftDown: 'leftDown',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {typeof AttributeNames[keyof typeof AttributeNames]} AttributeName
|
|
45
|
+
*/
|
|
46
|
+
export const AttributeNames = {
|
|
47
|
+
/** @type {'cube-type'} */
|
|
48
|
+
cubeType: 'cube-type',
|
|
49
|
+
/** @type {'piece-gap'} */
|
|
50
|
+
pieceGap: 'piece-gap',
|
|
51
|
+
/** @type {'animation-speed-ms'} */
|
|
52
|
+
animationSpeed: 'animation-speed-ms',
|
|
53
|
+
/** @type {'animation-style'} */
|
|
54
|
+
animationStyle: 'animation-style',
|
|
55
|
+
/** @type {'camera-speed-ms'} */
|
|
56
|
+
cameraSpeed: 'camera-speed-ms',
|
|
57
|
+
/** @type {'camera-radius'} */
|
|
58
|
+
cameraRadius: 'camera-radius',
|
|
59
|
+
/** @type {'camera-field-of-view'} */
|
|
60
|
+
cameraFieldOfView: 'camera-field-of-view',
|
|
61
|
+
/** @type {'camera-peek-angle-horizontal'} */
|
|
62
|
+
cameraPeekAngleHorizontal: 'camera-peek-angle-horizontal',
|
|
63
|
+
/** @type {'camera-peek-angle-vertical'} */
|
|
64
|
+
cameraPeekAngleVertical: 'camera-peek-angle-vertical',
|
|
65
|
+
/** @type {'logo'} */
|
|
66
|
+
logo: 'logo',
|
|
67
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @typedef {import("./constants").PeekState} PeekState */
|
|
3
|
+
/** @typedef {import("./constants").PeekAction} PeekAction */
|
|
4
|
+
/** @typedef {import("./constants").CameraOptions} CameraOptions */
|
|
5
|
+
/** @typedef {import('./constants').AnimationStyle} AnimationStyle */
|
|
6
|
+
export { RubiksCubeElement } from './rubiksCubeElement';
|
|
7
|
+
export { AttributeNames, PeekStates, PeekActions, AnimationStyles } from './constants';
|