@houstonp/rubiks-cube 2.0.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.
Files changed (76) hide show
  1. package/README.md +494 -63
  2. package/package.json +22 -12
  3. package/src/core/index.js +478 -0
  4. package/src/rubiksCube/index.js +3 -0
  5. package/src/rubiksCube/rubiksCubeController.js +111 -0
  6. package/src/rubiksCube3D/centerPiece.js +79 -0
  7. package/src/rubiksCube3D/cornerPiece.js +114 -0
  8. package/src/rubiksCube3D/cubeConfig.js +87 -0
  9. package/src/rubiksCube3D/cubeSettings.js +30 -0
  10. package/src/rubiksCube3D/edgePiece.js +51 -0
  11. package/src/rubiksCube3D/index.js +3 -0
  12. package/src/rubiksCube3D/rubiksCube3D.js +383 -0
  13. package/src/rubiksCube3D/sticker.js +38 -0
  14. package/src/state/index.js +4 -0
  15. package/src/state/rubiksCubeState.js +471 -0
  16. package/src/state/slice.js +236 -0
  17. package/src/state/stickerState.js +185 -0
  18. package/src/{cameraState.js → webComponent/cameraState.js} +17 -25
  19. package/src/webComponent/constants.js +67 -0
  20. package/src/{debouncer.js → webComponent/debouncer.js} +1 -1
  21. package/src/webComponent/index.js +7 -0
  22. package/src/webComponent/rubiksCubeElement.js +379 -0
  23. package/src/{settings.js → webComponent/settings.js} +47 -22
  24. package/tests/common.js +10 -0
  25. package/tests/core.test.js +56 -0
  26. package/tests/rubiksCube.solves.test.js +41 -0
  27. package/tests/rubiksCube3D.solves.test.js +185 -0
  28. package/tests/rubiksCubeState.solves.test.js +35 -0
  29. package/tests/setup.js +36 -0
  30. package/tests/testScrambles.js +194 -0
  31. package/types/core/index.d.ts +451 -0
  32. package/types/rubiksCube/index.d.ts +3 -0
  33. package/types/rubiksCube/rubiksCubeController.d.ts +62 -0
  34. package/types/rubiksCube3D/centerPiece.d.ts +27 -0
  35. package/types/rubiksCube3D/cornerPiece.d.ts +38 -0
  36. package/types/rubiksCube3D/cubeConfig.d.ts +32 -0
  37. package/types/rubiksCube3D/cubeSettings.d.ts +33 -0
  38. package/types/rubiksCube3D/edgePiece.d.ts +18 -0
  39. package/types/rubiksCube3D/index.d.ts +3 -0
  40. package/types/rubiksCube3D/rubiksCube3D.d.ts +120 -0
  41. package/types/rubiksCube3D/sticker.d.ts +18 -0
  42. package/types/state/index.d.ts +5 -0
  43. package/types/state/rubiksCubeState.d.ts +108 -0
  44. package/types/state/slice.d.ts +46 -0
  45. package/types/state/stickerState.d.ts +34 -0
  46. package/types/webComponent/cameraState.d.ts +22 -0
  47. package/types/webComponent/constants.d.ts +57 -0
  48. package/types/webComponent/index.d.ts +6 -0
  49. package/types/webComponent/rubiksCubeElement.d.ts +89 -0
  50. package/types/{settings.d.ts → webComponent/settings.d.ts} +9 -8
  51. package/src/core.js +0 -127
  52. package/src/cube/cube.js +0 -324
  53. package/src/cube/cubeRotation.js +0 -79
  54. package/src/cube/cubeSettings.js +0 -18
  55. package/src/cube/cubeState.js +0 -192
  56. package/src/cube/slice.js +0 -143
  57. package/src/index.js +0 -496
  58. package/src/schema.js +0 -22
  59. package/src/threejs/materials.js +0 -54
  60. package/src/threejs/pieces.js +0 -100
  61. package/src/threejs/stickers.js +0 -40
  62. package/types/cameraState.d.ts +0 -19
  63. package/types/core.d.ts +0 -125
  64. package/types/cube/cube.d.ts +0 -102
  65. package/types/cube/cubeRotation.d.ts +0 -33
  66. package/types/cube/cubeSettings.d.ts +0 -17
  67. package/types/cube/cubeState.d.ts +0 -16
  68. package/types/cube/slice.d.ts +0 -15
  69. package/types/index.d.ts +0 -65
  70. package/types/schema.d.ts +0 -11
  71. package/types/threejs/materials.d.ts +0 -21
  72. package/types/threejs/pieces.d.ts +0 -28
  73. package/types/threejs/stickers.d.ts +0 -6
  74. /package/src/{globals.ts → webComponent/globals.ts} +0 -0
  75. /package/types/{debouncer.d.ts → webComponent/debouncer.d.ts} +0 -0
  76. /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 { PeekStates, PeekTypes } from './core';
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 {import("./core").PeekType} peekType
18
+ * @param {PeekAction} action
18
19
  */
19
- peekCamera(peekType) {
20
- switch (peekType) {
21
- case PeekTypes.Horizontal:
20
+ peekCamera(action) {
21
+ switch (action) {
22
+ case PeekActions.Horizontal:
22
23
  this.Right = !this.Right;
23
24
  break;
24
- case PeekTypes.Vertical:
25
+ case PeekActions.Vertical:
25
26
  this.Up = !this.Up;
26
27
  break;
27
- case PeekTypes.Right:
28
+ case PeekActions.Right:
28
29
  this.Right = true;
29
30
  break;
30
- case PeekTypes.Left:
31
+ case PeekActions.Left:
31
32
  this.Right = false;
32
33
  break;
33
- case PeekTypes.Up:
34
+ case PeekActions.Up:
34
35
  this.Up = true;
35
36
  break;
36
- case PeekTypes.Down:
37
+ case PeekActions.Down:
37
38
  this.Up = false;
38
39
  break;
39
- case PeekTypes.RightUp:
40
+ case PeekActions.RightUp:
40
41
  this.Right = true;
41
42
  this.Up = true;
42
43
  break;
43
- case PeekTypes.RightDown:
44
+ case PeekActions.RightDown:
44
45
  this.Right = true;
45
46
  this.Up = false;
46
47
  break;
47
- case PeekTypes.LeftUp:
48
+ case PeekActions.LeftUp:
48
49
  this.Right = false;
49
50
  this.Up = true;
50
51
  break;
51
- case PeekTypes.LeftDown:
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 {import("./core").PeekState}
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
- if (!this.Right && !this.Up) {
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
+ };
@@ -11,6 +11,6 @@ export function debounce(f, delay) {
11
11
  */
12
12
  return function (...args) {
13
13
  clearTimeout(timer);
14
- timer = setTimeout(() => f.apply(this, args), delay);
14
+ timer = window.setTimeout(() => f.apply(this, args), delay);
15
15
  };
16
16
  }
@@ -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';