@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.
Files changed (76) hide show
  1. package/README.md +304 -77
  2. package/package.json +18 -8
  3. package/src/{core.js → core/index.js} +72 -41
  4. package/src/rubiksCube/index.js +3 -0
  5. package/src/rubiksCube/rubiksCubeController.js +111 -0
  6. package/src/{three → rubiksCube3D}/centerPiece.js +37 -2
  7. package/src/{three → rubiksCube3D}/cornerPiece.js +56 -2
  8. package/src/rubiksCube3D/cubeConfig.js +87 -0
  9. package/src/rubiksCube3D/cubeSettings.js +30 -0
  10. package/src/{three → rubiksCube3D}/edgePiece.js +2 -1
  11. package/src/rubiksCube3D/index.js +3 -0
  12. package/src/rubiksCube3D/rubiksCube3D.js +383 -0
  13. package/src/{three → rubiksCube3D}/sticker.js +5 -4
  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/{camera → webComponent}/cameraState.js +17 -25
  19. package/src/webComponent/constants.js +67 -0
  20. package/src/webComponent/index.js +7 -0
  21. package/src/webComponent/rubiksCubeElement.js +379 -0
  22. package/src/{settings.js → webComponent/settings.js} +36 -23
  23. package/tests/common.js +3 -20
  24. package/tests/core.test.js +56 -0
  25. package/tests/rubiksCube.solves.test.js +41 -0
  26. package/tests/rubiksCube3D.solves.test.js +185 -0
  27. package/tests/rubiksCubeState.solves.test.js +35 -0
  28. package/tests/testScrambles.js +194 -0
  29. package/types/{core.d.ts → core/index.d.ts} +45 -48
  30. package/types/rubiksCube/index.d.ts +3 -0
  31. package/types/rubiksCube/rubiksCubeController.d.ts +62 -0
  32. package/types/rubiksCube3D/centerPiece.d.ts +27 -0
  33. package/types/rubiksCube3D/cornerPiece.d.ts +38 -0
  34. package/types/rubiksCube3D/cubeConfig.d.ts +32 -0
  35. package/types/rubiksCube3D/cubeSettings.d.ts +33 -0
  36. package/types/{three → rubiksCube3D}/edgePiece.d.ts +5 -3
  37. package/types/rubiksCube3D/index.d.ts +3 -0
  38. package/types/rubiksCube3D/rubiksCube3D.d.ts +120 -0
  39. package/types/rubiksCube3D/sticker.d.ts +18 -0
  40. package/types/state/index.d.ts +5 -0
  41. package/types/state/rubiksCubeState.d.ts +108 -0
  42. package/types/state/slice.d.ts +46 -0
  43. package/types/state/stickerState.d.ts +34 -0
  44. package/types/webComponent/cameraState.d.ts +22 -0
  45. package/types/webComponent/constants.d.ts +57 -0
  46. package/types/webComponent/index.d.ts +6 -0
  47. package/types/webComponent/rubiksCubeElement.d.ts +89 -0
  48. package/types/{settings.d.ts → webComponent/settings.d.ts} +5 -8
  49. package/src/cube/animationSlice.js +0 -205
  50. package/src/cube/animationState.js +0 -96
  51. package/src/cube/cubeSettings.js +0 -19
  52. package/src/cube/cubeState.js +0 -337
  53. package/src/cube/stickerState.js +0 -188
  54. package/src/index.js +0 -621
  55. package/src/three/cube.js +0 -492
  56. package/tests/cube.five.test.js +0 -126
  57. package/tests/cube.four.test.js +0 -126
  58. package/tests/cube.seven.test.js +0 -126
  59. package/tests/cube.six.test.js +0 -126
  60. package/tests/cube.three.test.js +0 -151
  61. package/tests/cube.two.test.js +0 -125
  62. package/types/camera/cameraState.d.ts +0 -19
  63. package/types/cube/animationSlice.d.ts +0 -26
  64. package/types/cube/animationState.d.ts +0 -41
  65. package/types/cube/cubeSettings.d.ts +0 -17
  66. package/types/cube/cubeState.d.ts +0 -47
  67. package/types/cube/stickerState.d.ts +0 -21
  68. package/types/index.d.ts +0 -87
  69. package/types/three/centerPiece.d.ts +0 -15
  70. package/types/three/cornerPiece.d.ts +0 -24
  71. package/types/three/cube.d.ts +0 -130
  72. package/types/three/sticker.d.ts +0 -15
  73. /package/src/{debouncer.js → webComponent/debouncer.js} +0 -0
  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,89 @@
1
+ /// <reference path="globals.d.ts" preserve="true" />
2
+ export class RubiksCubeElement extends HTMLElement {
3
+ /**
4
+ * @param {string} tagName the name of the tag to register the web component under
5
+ */
6
+ static register(tagName?: string): void;
7
+ static get observedAttributes(): ("cube-type" | "piece-gap" | "animation-speed-ms" | "animation-style" | "camera-speed-ms" | "camera-radius" | "camera-field-of-view" | "camera-peek-angle-horizontal" | "camera-peek-angle-vertical" | "logo")[];
8
+ /** @private @type {HTMLCanvasElement} */
9
+ private canvas;
10
+ /** @private @type {Settings} */
11
+ private settings;
12
+ /** @private @type {RubiksCube3D?} */
13
+ private _rubiksCube3D;
14
+ /** @private @type {RubiksCubeController?} */
15
+ private _rubiksCube;
16
+ connectedCallback(): void;
17
+ /**
18
+ * @param {string} name
19
+ * @param {string?} oldVal
20
+ * @param {string?} newVal
21
+ * */
22
+ attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
23
+ /** @private */
24
+ private animateCameraSetting;
25
+ /** @private */
26
+ private animateCameraRadius;
27
+ /** @private */
28
+ private updateCameraFOV;
29
+ /** @internal @typedef {{eventId: string, move: Movement, reason: string}} MovementFailedEventData */
30
+ /**
31
+ * @param {Movement} move
32
+ * @param {AnimationOptions} [options]
33
+ * @returns {Promise<string>}
34
+ */
35
+ move(move: Movement, options?: AnimationOptions): Promise<string>;
36
+ /**
37
+ * @param {Rotation} rotation
38
+ * @param {AnimationOptions} [options]
39
+ * @returns {Promise<string>}
40
+ */
41
+ rotate(rotation: Rotation, options?: AnimationOptions): Promise<string>;
42
+ /**
43
+ * @returns {string}
44
+ */
45
+ reset(): string;
46
+ /**
47
+ * @param {string} kociembaState
48
+ * @returns {boolean}
49
+ */
50
+ setState(kociembaState: string): boolean;
51
+ /**
52
+ * @returns {string}
53
+ */
54
+ getState(): string;
55
+ /**
56
+ * @param {CubeType} cubeType
57
+ * @returns {string}
58
+ */
59
+ setType(cubeType: CubeType): string;
60
+ /**
61
+ * @private
62
+ **/
63
+ private _rebuild;
64
+ /** @internal @typedef {{eventId: string, action: PeekAction, options: CameraOptions?}} CameraPeekEventData */
65
+ /** @internal @typedef {{eventId: string, peekState: PeekState }} CameraPeekCompleteEventData */
66
+ /**
67
+ * Animates the camera to a new "peek" position.
68
+ *
69
+ * The camera tracks two independent boolean axes (horizontal: Right/Left, vertical: Up/Down), giving four
70
+ * reachable positions (the {@link PeekState}s). Each `PeekAction` operates on this state machine: actions like
71
+ * `RightUp` set both axes; `Up`/`Right`/`Left`/`Down` set one axis and leave the other untouched;
72
+ * `Horizontal`/`Vertical` toggle one axis relative to its current value. The result of a partial action
73
+ * therefore depends on the current peek state.
74
+ *
75
+ * @param {PeekAction} action
76
+ * @param {CameraOptions?} options
77
+ * @returns {Promise<PeekState>}
78
+ */
79
+ peek(action: PeekAction, options?: CameraOptions | null): Promise<PeekState>;
80
+ /** @private */
81
+ private init;
82
+ }
83
+ import type { Movement } from '../core';
84
+ import type { AnimationOptions } from '../rubiksCube';
85
+ import type { Rotation } from '../core';
86
+ import type { CubeType } from '../core';
87
+ import type { PeekAction } from './constants';
88
+ import type { CameraOptions } from './constants';
89
+ import type { PeekState } from './constants';
@@ -1,12 +1,6 @@
1
1
  export default class Settings {
2
- /** @type {import("./core").CubeType} */
3
- cubeType: import("./core").CubeType;
4
- /** @type {number} */
5
- pieceGap: number;
6
- /** @type {number} */
7
- animationSpeedMs: number;
8
- /** @type {import("./core").AnimationStyle} */
9
- animationStyle: import("./core").AnimationStyle;
2
+ /** @type {RubiksCube3DSettings} */
3
+ rubiksCube3DSettings: RubiksCube3DSettings;
10
4
  /** @type {number} */
11
5
  cameraSpeedMs: number;
12
6
  /** @type {number} */
@@ -35,4 +29,7 @@ export default class Settings {
35
29
  setCameraPeekAngleVertical(value: string | null): void;
36
30
  /** @param {string | null} value in ms */
37
31
  setCameraFieldOfView(value: string | null): void;
32
+ /** @param {string | null} value in ms */
33
+ setLogo(value: string | null): void;
38
34
  }
35
+ import RubiksCube3DSettings from '../rubiksCube3D/cubeSettings';
@@ -1,205 +0,0 @@
1
- // @ts-check
2
- import { Rotations } from '../core';
3
- import { getAllLayers } from './cubeState';
4
-
5
- /** @typedef {typeof Axi[keyof typeof Axi]} Axis */
6
- export const Axi = Object.freeze({
7
- x: 'x',
8
- y: 'y',
9
- z: 'z',
10
- });
11
-
12
- /** @typedef {{axis: Axis, layers: number[], direction: number}} Slice */
13
-
14
- /**
15
- * @param {import('../core').Movement } outerBlockMovement
16
- * @param {import('../core').CubeType} cubeType
17
- * @param {boolean} prioritiseStandardMovement
18
- * @returns {Slice | undefined}
19
- */
20
- export function GetLayerSlice(outerBlockMovement, cubeType, prioritiseStandardMovement = false) {
21
- const layers = getAllLayers(cubeType);
22
- const result = RegExp(`^([1234567]|[123456]-[1234567])?([RLUDFB]w|[RLUDFBMES]|[rludfbmes])([123])?(\')?$`).exec(outerBlockMovement);
23
- if (result == null) {
24
- console.error(`Failed to parse outerBlockMovement. invalid movement: ${outerBlockMovement}`);
25
- return undefined;
26
- }
27
- let layerRangeLower = undefined;
28
- let layerRangeUpper = undefined;
29
- let layerNumber = undefined;
30
- if (result[1]?.includes('-')) {
31
- layerRangeLower = parseInt(result[1][0]);
32
- layerRangeUpper = parseInt(result[1][2]);
33
- } else {
34
- layerNumber = result[1] ? parseInt(result[1]) : undefined;
35
- } // some input validation
36
-
37
- if (layerRangeLower != null && layerRangeUpper != null) {
38
- if (layerRangeLower >= layerRangeUpper || layerRangeUpper > layers.length || layerRangeLower > layers.length - 1) {
39
- console.error(
40
- `${outerBlockMovement} is not valid for the current cubeType: ${cubeType}. For range inputs like x-yr it should follow that 1 <= x < y <= ${layers.length}.`,
41
- );
42
- return undefined;
43
- }
44
- }
45
- if (layerNumber != null && layerNumber > layers.length) {
46
- console.error(
47
- `${outerBlockMovement} is not valid for the current cubeType: ${cubeType}. For inputs like xR it should follow that x <= ${layers.length}.`,
48
- );
49
- return undefined;
50
- }
51
-
52
- const movementType = result[2];
53
- const rotationNumber = result[3] ? parseInt(result[3]) % 4 : 1;
54
- const isPrime = result[4] === "'";
55
- const direction = (isPrime ? -1 : 1) * rotationNumber;
56
-
57
- let axis = undefined;
58
- switch (movementType) {
59
- case 'R':
60
- case 'Rw':
61
- case 'r':
62
- case 'L':
63
- case 'Lw':
64
- case 'l':
65
- case 'M':
66
- case 'm':
67
- axis = Axi.x;
68
- break;
69
- case 'U':
70
- case 'Uw':
71
- case 'u':
72
- case 'D':
73
- case 'Dw':
74
- case 'd':
75
- case 'E':
76
- case 'e':
77
- axis = Axi.y;
78
- break;
79
- case 'F':
80
- case 'Fw':
81
- case 'f':
82
- case 'B':
83
- case 'Bw':
84
- case 'b':
85
- case 'S':
86
- case 's':
87
- axis = Axi.z;
88
- break;
89
- default:
90
- console.error(`${outerBlockMovement} is invalid. Invalid movementType ${movementType}.`);
91
- }
92
- if (axis == null) {
93
- return undefined;
94
- }
95
-
96
- switch (movementType) {
97
- case 'R':
98
- case 'U':
99
- case 'F':
100
- layerNumber = layerNumber ? layerNumber : 1;
101
- var layer = layers[layers.length - layerNumber];
102
- return { axis, layers: [layer], direction: -direction };
103
- case 'L':
104
- case 'D':
105
- case 'B':
106
- layerNumber = layerNumber ? layerNumber : 1;
107
- var layer = layers[layerNumber - 1];
108
- return { axis, layers: [layer], direction };
109
- case 'Rw':
110
- case 'Uw':
111
- case 'Fw':
112
- layerNumber = layerNumber ? layerNumber : 2;
113
- var sliceLayers = layers.slice(layers.length - layerNumber);
114
- if (layerRangeLower != null && layerRangeUpper != null) {
115
- var sliceLayers = layers.slice(layers.length - layerRangeUpper, layers.length - (layerRangeLower - 1));
116
- }
117
- return { axis, layers: sliceLayers, direction: -direction };
118
- case 'r':
119
- case 'u':
120
- case 'f':
121
- var defaultLayerNumber = prioritiseStandardMovement ? layers.length - 1 : 2;
122
- layerNumber = layerNumber ? layerNumber : defaultLayerNumber;
123
- var sliceLayers = layers.slice(layers.length - layerNumber);
124
- if (layerRangeLower != null && layerRangeUpper != null) {
125
- var sliceLayers = layers.slice(layers.length - layerRangeUpper, layers.length - (layerRangeLower - 1));
126
- }
127
- return { axis, layers: sliceLayers, direction: -direction };
128
- case 'Lw':
129
- case 'Dw':
130
- case 'Bw':
131
- layerNumber = layerNumber ? layerNumber : 2;
132
- var sliceLayers = layers.slice(0, layerNumber);
133
- if (layerRangeLower != null && layerRangeUpper != null) {
134
- var sliceLayers = layers.slice(layerRangeLower - 1, layerRangeUpper);
135
- }
136
- return { axis, layers: sliceLayers, direction };
137
- case 'l':
138
- case 'd':
139
- case 'b':
140
- var defaultLayerNumber = prioritiseStandardMovement ? layers.length - 1 : 2;
141
- layerNumber = layerNumber ? layerNumber : defaultLayerNumber;
142
- var sliceLayers = layers.slice(0, layerNumber);
143
- if (layerRangeLower != null && layerRangeUpper != null) {
144
- var sliceLayers = layers.slice(layerRangeLower - 1, layerRangeUpper);
145
- }
146
- return { axis, layers: sliceLayers, direction };
147
- case 'M':
148
- case 'E':
149
- var defaultLayerNumber = prioritiseStandardMovement ? Math.floor(layers.length / 2) : 1;
150
- layerNumber = layerNumber ? layerNumber : defaultLayerNumber;
151
- var lower = Math.max(Math.floor(layers.length / 2) - (layerNumber - 1), 1);
152
- var upper = Math.min(Math.ceil(layers.length / 2) + (layerNumber - 1), layers.length - 1);
153
- var sliceLayers = layers.slice(lower, upper);
154
- return { axis, layers: sliceLayers, direction };
155
- case 'm':
156
- case 'e':
157
- layerNumber = layerNumber ? layerNumber : 1;
158
- var sliceLayers = layers.slice(layerNumber, layers.length - layerNumber);
159
- return { axis, layers: sliceLayers, direction };
160
- case 'S':
161
- var defaultLayerNumber = prioritiseStandardMovement ? Math.floor(layers.length / 2) : 1;
162
- layerNumber = layerNumber ? layerNumber : defaultLayerNumber;
163
- var lower = Math.max(Math.floor(layers.length / 2) - (layerNumber - 1), 1);
164
- var upper = Math.min(Math.ceil(layers.length / 2) + (layerNumber - 1), layers.length - 1);
165
- var sliceLayers = layers.slice(lower, upper);
166
- return { axis, layers: sliceLayers, direction: -direction };
167
- case 's':
168
- layerNumber = layerNumber ? layerNumber : 1;
169
- var sliceLayers = layers.slice(layerNumber, layers.length - layerNumber);
170
- return { axis, layers: sliceLayers, direction: -direction };
171
-
172
- default:
173
- console.error(`${outerBlockMovement} is invalid. Invalid movementType ${movementType}.`);
174
- return undefined;
175
- }
176
- }
177
-
178
- /**
179
- * @param {import("../core").Rotation} rotation
180
- * @param {import('../core').CubeType} cubeType
181
- * @returns {Slice | undefined}
182
- */
183
- export function GetRotationSlice(rotation, cubeType) {
184
- const result = RegExp(`^([xyz])(\\d)?(\')?$`).exec(rotation);
185
- if (result == null) {
186
- console.error(`Failed to parse rotation. invalid rotation: [${rotation}]`);
187
- return undefined;
188
- }
189
- const rotationType = result[1];
190
- const rotationNumber = result[2] ? parseInt(result[2]) : 1;
191
-
192
- const isPrime = result[3] === "'";
193
- const direction = (isPrime ? 1 : -1) * (rotationNumber % 4);
194
- switch (rotationType) {
195
- case Rotations.x:
196
- return { axis: Axi.x, layers: getAllLayers(cubeType), direction };
197
- case Rotations.y:
198
- return { axis: Axi.y, layers: getAllLayers(cubeType), direction };
199
- case Rotations.z:
200
- return { axis: Axi.z, layers: getAllLayers(cubeType), direction };
201
- default:
202
- console.error(`Failed to get rotation slice. invalid rotationType: ${rotationType}`);
203
- return undefined;
204
- }
205
- }
@@ -1,96 +0,0 @@
1
- // @ts-check
2
- /** @typedef {AnimationStatus[keyof AnimationStatus]} AnimationStatusType */
3
- export const AnimationStatus = Object.freeze({
4
- Pending: 'pending',
5
- Initialised: 'initialised',
6
- InProgress: 'inProgress',
7
- Complete: 'complete',
8
- Disposed: 'disposed',
9
- });
10
-
11
- export class AnimationState {
12
- /**
13
- * @param {import('./animationSlice').Slice} slice
14
- * @param {((state: string) => void )} completedCallback
15
- * @param {((reason: string) => void )} failedCallback
16
- */
17
- constructor(slice, completedCallback, failedCallback) {
18
- /** @type {((state: string) => void )} */
19
- this.completedCallback = completedCallback;
20
- /** @type {((reason: string) => void )} */
21
- this.failedCallback = failedCallback;
22
- /** @type {import('./animationSlice').Slice} */
23
- this.slice = slice;
24
- /** @type {AnimationStatusType} */
25
- this.status = AnimationStatus.Pending;
26
- /** @type {number} */
27
- this.timestampMs = performance.now();
28
- /** @type {number | undefined} */
29
- this._lastUpdatedTimeMs = undefined;
30
- /** @type {number} */
31
- this._rotationPercentage = 0;
32
- }
33
-
34
- initialise() {
35
- this._lastUpdatedTimeMs = performance.now();
36
- this.status = AnimationStatus.Initialised;
37
- }
38
-
39
- /** @param {string} state */
40
- complete(state) {
41
- this.status = AnimationStatus.Disposed;
42
- this.completedCallback(state);
43
- }
44
-
45
- /** @param {string} reason */
46
- abort(reason) {
47
- this.status = AnimationStatus.Complete;
48
- this.failedCallback(reason);
49
- }
50
-
51
- /**
52
- * @param {number} speedMs
53
- * @returns {number} rotationIncrement
54
- */
55
- update(speedMs) {
56
- if (speedMs < 0) {
57
- throw new Error(`Speed is negative.`);
58
- }
59
- if (this.status === AnimationStatus.Initialised) {
60
- this.status = AnimationStatus.InProgress;
61
- }
62
-
63
- if (this.status !== AnimationStatus.InProgress) {
64
- throw new Error(`Cannot update AnimationState. Invalid Status - [${this.status}]`);
65
- }
66
-
67
- if (this._lastUpdatedTimeMs == null) {
68
- throw new Error(`Cannot update AnimationState. LastUpdated is not set. Value: "${this._lastUpdatedTimeMs}".`);
69
- }
70
-
71
- var intervalMs = performance.now() - this._lastUpdatedTimeMs;
72
- this._lastUpdatedTimeMs = performance.now();
73
-
74
- var increment = 100 - this._rotationPercentage;
75
- if (speedMs != 0) {
76
- var potentialIncrement = (intervalMs / speedMs) * 100;
77
- if (potentialIncrement + this._rotationPercentage < 100) {
78
- increment = potentialIncrement;
79
- }
80
- }
81
-
82
- if (this._rotationPercentage + increment > 100) {
83
- throw new Error(
84
- `Cannot update AnimationState. Next increment would cause over rotation. Increment: ${increment}%. Current rotation: ${this._rotationPercentage}%.`,
85
- );
86
- }
87
-
88
- this._rotationPercentage += increment;
89
-
90
- if (this._rotationPercentage === 100) {
91
- this.status = AnimationStatus.Complete;
92
- }
93
-
94
- return increment;
95
- }
96
- }
@@ -1,19 +0,0 @@
1
- // @ts-check
2
- export default class CubeSettings {
3
- /**
4
- * @param {import('../core').CubeType} cubeType
5
- * @param {number} pieceGap
6
- * @param {number} animationSpeed
7
- * @param {import('../core').AnimationStyle} animationStyle
8
- */
9
- constructor(pieceGap, animationSpeed, animationStyle, cubeType) {
10
- /** @type {number} pieceGap */
11
- this.pieceGap = pieceGap;
12
- /** @type {import("../core").CubeType} */
13
- this.cubeType = cubeType;
14
- /** @type {number} pieceGap */
15
- this.animationSpeedMs = animationSpeed;
16
- /** @type {import('../core').AnimationStyle} pieceGap */
17
- this.animationStyle = animationStyle;
18
- }
19
- }