@houstonp/rubiks-cube 1.5.1 → 2.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/src/cube/cube.js CHANGED
@@ -1,38 +1,45 @@
1
- import { Group, Vector3 } from 'three';
1
+ // @ts-check
2
+ import { Group, Material, Mesh, Object3D, Vector3 } from 'three';
2
3
  import { createCoreMesh } from '../threejs/pieces';
3
4
  import { createCubeState } from './cubeState';
4
5
  import { CubeRotation } from './cubeRotation';
6
+ import CubeSettings from './cubeSettings';
7
+ import GetMovementSlice, { GetRotationSlice } from './slice';
8
+ import { Axi } from '../core';
5
9
 
10
+ /** @typedef {{ up: import('../core').Face[][], down: import('../core').Face[][], front: import('../core').Face[][], back: import('../core').Face[][], left: import('../core').Face[][], right: import('../core').Face[][] }} StickerState*/
6
11
  export default class Cube {
7
12
  /**
8
- * @param {import('../..').Settings} settings
13
+ * @param {CubeSettings} cubeSettings
9
14
  */
10
- constructor(settings) {
11
- /** @type {import('../..').Settings} */
12
- this.settings = settings;
15
+ constructor(cubeSettings) {
16
+ /** @type {CubeSettings} */
17
+ this.cubeSettings = cubeSettings;
13
18
  /** @type {Group} */
14
- this.group = this.createCubeGroup();
19
+ this.group = new Group();
15
20
  /** @type {Group} */
16
21
  this.rotationGroup = new Group();
17
22
  /** @type {CubeRotation[]} */
18
23
  this.rotationQueue = [];
19
24
  /** @type {CubeRotation | undefined} */
20
25
  this.currentRotation = undefined;
21
- /** @type {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] }} */
22
- this.currentState = this.getStickerState();
26
+
23
27
  /** @type {number | undefined} */
24
28
  this._matchSpeed = undefined;
25
29
  /** @type {number} */
26
- this._lastGap = settings.gap;
30
+ this._lastGap = cubeSettings.pieceGap;
31
+
32
+ // initialise threejs Objects
33
+ this.init();
34
+
35
+ /** @type {StickerState} */
36
+ this.currentState = this.getStickerState();
27
37
  }
28
38
 
29
39
  /**
30
- * creates a ThreeJS group with all the required pieces for a cube
31
- * @param {Group} group
32
- * @returns {Group}
40
+ * adds threejs objects to group
33
41
  */
34
- createCubeGroup(group) {
35
- var group = new Group();
42
+ init() {
36
43
  const core = createCoreMesh();
37
44
  core.userData = {
38
45
  position: { x: 0, y: 0, z: 0 },
@@ -41,14 +48,14 @@ export default class Cube {
41
48
  initialRotation: { x: 0, y: 0, z: 0 },
42
49
  type: 'core',
43
50
  };
44
- group.add(core);
51
+ this.group.add(core);
45
52
 
46
53
  for (const piece of createCubeState()) {
47
54
  var pieceGroup = piece.group;
48
55
  pieceGroup.position.set(
49
- piece.position.x * this.settings.pieceGap,
50
- piece.position.y * this.settings.pieceGap,
51
- piece.position.z * this.settings.pieceGap,
56
+ piece.position.x * this.cubeSettings.pieceGap,
57
+ piece.position.y * this.cubeSettings.pieceGap,
58
+ piece.position.z * this.cubeSettings.pieceGap,
52
59
  );
53
60
  pieceGroup.rotation.set(piece.rotation.x, piece.rotation.y, piece.rotation.z);
54
61
  pieceGroup.userData = {
@@ -58,42 +65,40 @@ export default class Cube {
58
65
  initialRotation: Object.assign({}, piece.rotation),
59
66
  type: piece.type,
60
67
  };
61
- group.add(pieceGroup);
68
+ this.group.add(pieceGroup);
62
69
  }
63
- return group;
70
+ return this.group;
64
71
  }
65
72
 
66
73
  /**
67
74
  * update the cube and continue any rotations
68
- * @returns {string}
69
75
  */
70
76
  update() {
71
77
  if (this.currentRotation === undefined) {
72
- if (this._lastGap !== this.settings.pieceGap) {
78
+ if (this._lastGap !== this.cubeSettings.pieceGap) {
73
79
  this.updateGap();
74
80
  }
75
81
  this.currentRotation = this.rotationQueue.shift();
76
82
  if (this.currentRotation === undefined) {
77
83
  this._matchSpeed = undefined; // reset speed for the match animation options
78
- return undefined;
84
+ return;
79
85
  }
80
86
  }
81
87
  if (this.currentRotation.status === 'pending') {
82
- this.rotationGroup.add(...this.getRotationLayer(this.currentRotation.rotation));
88
+ this.rotationGroup.add(...this.getRotationLayer(this.currentRotation.slice));
83
89
  this.currentRotation.initialise();
84
90
  }
85
- if (this.currentRotation.status === 'initialised') {
91
+ if (this.currentRotation.status === 'initialised' || this.currentRotation.status === 'inProgress') {
86
92
  var speed = this.getRotationSpeed();
87
93
  this.currentRotation.update(this.rotationGroup, speed);
88
94
  }
89
95
  if (this.currentRotation.status === 'complete') {
90
96
  this.clearRotationGroup();
91
- var eventId = this.currentRotation.eventId;
92
- this.currentRotation = undefined;
93
97
  this.currentState = this.getStickerState();
94
- return eventId;
98
+ this.currentRotation.completedCallback(this.kociembaState);
99
+ this.currentRotation = undefined;
95
100
  }
96
- return undefined;
101
+ return;
97
102
  }
98
103
 
99
104
  /**
@@ -104,9 +109,9 @@ export default class Cube {
104
109
  if (this.currentRotation === undefined) {
105
110
  this.group.children.forEach((piece) => {
106
111
  var { x, y, z } = piece.userData.position;
107
- piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
112
+ piece.position.set(x * this.cubeSettings.pieceGap, y * this.cubeSettings.pieceGap, z * this.cubeSettings.pieceGap);
108
113
  });
109
- this._lastGap = this.settings.pieceGap;
114
+ this._lastGap = this.cubeSettings.pieceGap;
110
115
  }
111
116
  }
112
117
 
@@ -121,16 +126,17 @@ export default class Cube {
121
126
  * @returns {number}
122
127
  */
123
128
  getRotationSpeed() {
124
- if (this.settings.animationStyle === 'exponential') {
125
- return this.settings.animationSpeedMs / 2 ** this.rotationQueue.length;
129
+ if (this.cubeSettings.animationStyle === 'exponential') {
130
+ return this.cubeSettings.animationSpeedMs / 2 ** this.rotationQueue.length;
126
131
  }
127
- if (this.settings.animationStyle === 'next') {
128
- return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeedMs;
132
+ if (this.cubeSettings.animationStyle === 'next') {
133
+ return this.rotationQueue.length > 0 ? 0 : this.cubeSettings.animationSpeedMs;
129
134
  }
130
- if (this.settings.animationStyle === 'match') {
135
+ if (this.cubeSettings.animationStyle === 'match') {
131
136
  if (this.rotationQueue.length > 0) {
132
- var lastTimeStamp = this.currentRotation.timestampMs;
133
- var minGap = this._matchSpeed ?? this.settings.animationSpeedMs;
137
+ const rotation = /** @type {CubeRotation} */ (this.currentRotation);
138
+ const lastTimeStamp = rotation.timestampMs;
139
+ let minGap = this._matchSpeed ?? this.cubeSettings.animationSpeedMs;
134
140
  for (var i = 0; i < this.rotationQueue.length; i++) {
135
141
  var gap = this.rotationQueue[i].timestampMs - lastTimeStamp;
136
142
  if (gap < minGap) {
@@ -142,29 +148,32 @@ export default class Cube {
142
148
  if (this._matchSpeed !== undefined) {
143
149
  return this._matchSpeed;
144
150
  }
145
- return this.settings.animationSpeedMs;
151
+ return this.cubeSettings.animationSpeedMs;
146
152
  }
147
- if (this.settings.animationStyle === 'fixed') {
148
- return this.settings.animationSpeedMs;
153
+ if (this.cubeSettings.animationStyle === 'fixed') {
154
+ return this.cubeSettings.animationSpeedMs;
149
155
  }
150
- return this.settings.animationSpeedMs;
156
+ return this.cubeSettings.animationSpeedMs;
151
157
  }
152
158
 
153
159
  /**
154
160
  * Complete the current rotation and reset the cube
161
+ * @param {(state:string) => boolean} completedCallback
155
162
  * @returns {void}
156
163
  */
157
- reset() {
164
+ reset(completedCallback) {
165
+ this.rotationQueue.forEach((cubeRotation) => cubeRotation.failedCallback('State reset during action'));
158
166
  this.rotationQueue = [];
159
167
  if (this.currentRotation) {
160
168
  this.currentRotation.update(this.rotationGroup, 0);
161
169
  this.clearRotationGroup();
170
+ this.currentRotation.failedCallback('State reset during action');
162
171
  this.currentRotation = undefined;
163
172
  }
164
173
  this.group.children.forEach((piece) => {
165
174
  const { x, y, z } = piece.userData.initialPosition;
166
175
  const { x: u, y: v, z: w } = piece.userData.initialRotation;
167
- piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
176
+ piece.position.set(x * this.cubeSettings.pieceGap, y * this.cubeSettings.pieceGap, z * this.cubeSettings.pieceGap);
168
177
  piece.rotation.set(u, v, w);
169
178
  piece.userData.position.x = x;
170
179
  piece.userData.position.y = y;
@@ -173,6 +182,11 @@ export default class Cube {
173
182
  piece.userData.rotation.y = v;
174
183
  piece.userData.rotation.z = w;
175
184
  });
185
+
186
+ this.currentState = this.getStickerState();
187
+ if (!completedCallback(this.kociembaState)) {
188
+ console.error('Failed to invoke reset completedCallback');
189
+ }
176
190
  }
177
191
 
178
192
  /**
@@ -180,8 +194,13 @@ export default class Cube {
180
194
  * @returns {void}
181
195
  */
182
196
  clearRotationGroup() {
197
+ if (this.currentRotation == null) {
198
+ console.error('cannot clear rotation when rotation is null');
199
+ return;
200
+ }
183
201
  if (this.currentRotation.status != 'complete') {
184
- throw Error('cannot clear rotation group while rotating');
202
+ console.error('cannot clear rotation group while rotating');
203
+ return;
185
204
  }
186
205
  this.rotationGroup.children.forEach((piece) => {
187
206
  piece.getWorldPosition(piece.position);
@@ -202,51 +221,78 @@ export default class Cube {
202
221
  }
203
222
 
204
223
  /**
205
- * @param {string} eventId
206
- * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} input
224
+ * @param {string} eventId
225
+ * @param {import('../core').Rotation} rotation
226
+ * @param {((state: string) => void )} completedCallback
227
+ * @param {((reason: string) => void )} failedCallback
228
+ */
229
+ rotation(eventId, rotation, completedCallback, failedCallback) {
230
+ const slice = GetRotationSlice(rotation);
231
+ this.rotationQueue.push(new CubeRotation(eventId, slice, completedCallback, failedCallback));
232
+ }
233
+
234
+ /**
235
+ * @param {string} eventId
236
+ * @param {import('../core').Movement} movement
237
+ * @param {((state: string) => void )} completedCallback
238
+ * @param {((reason: string) => void )} failedCallback
207
239
  */
208
- rotate(eventId, input) {
209
- this.rotationQueue.push(new CubeRotation(eventId, input));
240
+ movement(eventId, movement, completedCallback, failedCallback) {
241
+ const slice = GetMovementSlice(movement);
242
+ this.rotationQueue.push(new CubeRotation(eventId, slice, completedCallback, failedCallback));
210
243
  }
211
244
 
212
245
  /**
213
- * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}}
246
+ * @param {import('./slice').Slice} slice
214
247
  * @returns {Object3D[]}
215
248
  */
216
- getRotationLayer({ axis, layers, direction }) {
217
- if (layers.length === 0) {
249
+ getRotationLayer(slice) {
250
+ if (slice.layers.length === 0) {
218
251
  return [...this.group.children];
219
252
  }
220
253
  return this.group.children.filter((piece) => {
221
- if (axis === 'x') {
222
- return layers.includes(Math.round(piece.userData.position.x));
223
- } else if (axis === 'y') {
224
- return layers.includes(Math.round(piece.userData.position.y));
225
- } else if (axis === 'z') {
226
- return layers.includes(Math.round(piece.userData.position.z));
254
+ if (slice.axis === Axi.x) {
255
+ const roundedPos = /** @type {(-1|0|1)} */ (Math.round(piece.userData.position.x));
256
+ return slice.layers.includes(roundedPos);
257
+ } else if (slice.axis === Axi.y) {
258
+ const roundedPos = /** @type {(-1|0|1)} */ (Math.round(piece.userData.position.y));
259
+ return slice.layers.includes(roundedPos);
260
+ } else if (slice.axis === Axi.z) {
261
+ const roundedPos = /** @type {(-1|0|1)} */ (Math.round(piece.userData.position.z));
262
+ return slice.layers.includes(roundedPos);
227
263
  }
228
264
  return false;
229
265
  });
230
266
  }
231
267
 
232
268
  /**
233
- * @returns {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] }}
269
+ * @returns {string}
270
+ */
271
+ get kociembaState() {
272
+ return this.toKociemba(this.currentState);
273
+ }
274
+
275
+ /**
276
+ * @param {StickerState} stickerState
277
+ * @returns {string}
278
+ */
279
+ toKociemba(stickerState) {
280
+ return `${stickerState.up.flat().join('')}${stickerState.right.flat().join('')}${stickerState.front.flat().join('')}${stickerState.down.flat().join('')}${stickerState.left.flat().join('')}${stickerState.back.flat().join('')}`;
281
+ }
282
+
283
+ /**
284
+ * @returns {StickerState}
234
285
  */
235
286
  getStickerState() {
236
- const state = {
237
- up: [[], [], []],
238
- down: [[], [], []],
239
- front: [[], [], []],
240
- back: [[], [], []],
241
- left: [[], [], []],
242
- right: [[], [], []],
243
- };
287
+ /** @type {StickerState} */
288
+ let state = { up: [[], [], []], down: [[], [], []], front: [[], [], []], back: [[], [], []], left: [[], [], []], right: [[], [], []] };
244
289
  this.group.children.forEach((piece) => {
245
290
  if (piece.userData.type === 'core') {
246
291
  return;
247
292
  }
248
- piece.children.forEach((mesh) => {
249
- if (mesh.userData.type === 'sticker') {
293
+ piece.children.forEach((object3D) => {
294
+ if (object3D.userData.type === 'sticker') {
295
+ const mesh = /** @type {Mesh} */ (object3D);
250
296
  const piecepos = new Vector3();
251
297
  piecepos.copy(piece.userData.position);
252
298
  piecepos.round();
@@ -255,18 +301,20 @@ export default class Cube {
255
301
  stickerpos.sub(piecepos);
256
302
  stickerpos.multiplyScalar(2);
257
303
  stickerpos.round();
304
+ const material = /** @type {Material} */ (mesh.material);
305
+ const materialUserData = /** @type {import('../threejs/materials').StickerUserData} */ (material.userData);
258
306
  if (stickerpos.x === 1) {
259
- state.right[1 - Math.round(piecepos.y)][1 - Math.round(piecepos.z)] = mesh.material.userData.face;
307
+ state.right[1 - Math.round(piecepos.y)][1 - Math.round(piecepos.z)] = materialUserData.face;
260
308
  } else if (stickerpos.x === -1) {
261
- state.left[1 - Math.round(piecepos.y)][1 + Math.round(piecepos.z)] = mesh.material.userData.face;
309
+ state.left[1 - Math.round(piecepos.y)][1 + Math.round(piecepos.z)] = materialUserData.face;
262
310
  } else if (stickerpos.y === 1) {
263
- state.up[1 + Math.round(piecepos.z)][1 + Math.round(piecepos.x)] = mesh.material.userData.face;
311
+ state.up[1 + Math.round(piecepos.z)][1 + Math.round(piecepos.x)] = materialUserData.face;
264
312
  } else if (stickerpos.y === -1) {
265
- state.down[1 - Math.round(piecepos.z)][1 + Math.round(piecepos.x)] = mesh.material.userData.face;
313
+ state.down[1 - Math.round(piecepos.z)][1 + Math.round(piecepos.x)] = materialUserData.face;
266
314
  } else if (stickerpos.z === 1) {
267
- state.front[1 - Math.round(piecepos.y)][1 + Math.round(piecepos.x)] = mesh.material.userData.face;
315
+ state.front[1 - Math.round(piecepos.y)][1 + Math.round(piecepos.x)] = materialUserData.face;
268
316
  } else if (stickerpos.z === -1) {
269
- state.back[1 - Math.round(piecepos.y)][1 - Math.round(piecepos.x)] = mesh.material.userData.face;
317
+ state.back[1 - Math.round(piecepos.y)][1 - Math.round(piecepos.x)] = materialUserData.face;
270
318
  }
271
319
  }
272
320
  });
@@ -1,20 +1,27 @@
1
+ // @ts-check
1
2
  import { Vector3, Group } from 'three';
2
3
 
3
4
  export class CubeRotation {
4
5
  /**
5
6
  * @param {string} eventId
6
- * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} rotationDetails
7
+ * @param {import('./slice').Slice} slice
8
+ * @param {((state: string) => void )} completedCallback
9
+ * @param {((reason: string) => void )} failedCallback
7
10
  */
8
- constructor(eventId, rotationDetails) {
11
+ constructor(eventId, slice, completedCallback, failedCallback) {
12
+ /** @type {((state: string) => void )} */
13
+ this.completedCallback = completedCallback;
14
+ /** @type {((reason: string) => void )} */
15
+ this.failedCallback = failedCallback;
9
16
  /** @type {string} */
10
17
  this.eventId = eventId;
11
- /** @type {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} */
12
- this.rotation = rotationDetails;
13
- /** @type {"pending" | "initialised" | "complete" | "disposed"} */
18
+ /** @type {import('./slice').Slice} */
19
+ this.slice = slice;
20
+ /** @type {"pending" | "initialised" | "inProgress" | "complete" | "disposed"} */
14
21
  this.status = 'pending';
15
22
  /** @type {number} */
16
23
  this.timestampMs = performance.now();
17
- /** @type {number} */
24
+ /** @type {number | undefined} */
18
25
  this._lastUpdatedTimeMs = undefined;
19
26
  /** @type {number} */
20
27
  this._rotationPercentage = 0;
@@ -31,6 +38,15 @@ export class CubeRotation {
31
38
  * @param {number} speedMs
32
39
  */
33
40
  update(rotationGroup, speedMs) {
41
+ if (this.status === 'initialised') {
42
+ this.status = 'inProgress';
43
+ }
44
+
45
+ if (this.status !== 'inProgress' || this._lastUpdatedTimeMs == null) {
46
+ console.error(`Cannot update cubeRotation. Status - [${this.status}]. LastUpdated - [${this._lastUpdatedTimeMs}].`);
47
+ return;
48
+ }
49
+
34
50
  var intervalMs = performance.now() - this._lastUpdatedTimeMs;
35
51
  this._lastUpdatedTimeMs = performance.now();
36
52
 
@@ -41,13 +57,13 @@ export class CubeRotation {
41
57
  increment = potentialIncrement;
42
58
  }
43
59
  }
44
- const rotationIncrement = (Math.abs(this.rotation.direction) * ((increment / 100) * Math.PI)) / 2;
60
+ const rotationIncrement = (Math.abs(this.slice.direction) * ((increment / 100) * Math.PI)) / 2;
45
61
  this._rotationPercentage += increment;
46
62
  rotationGroup.rotateOnWorldAxis(
47
63
  new Vector3(
48
- this.rotation.axis === 'x' ? this.rotation.direction : 0,
49
- this.rotation.axis === 'y' ? this.rotation.direction : 0,
50
- this.rotation.axis === 'z' ? this.rotation.direction : 0,
64
+ this.slice.axis === 'x' ? this.slice.direction : 0,
65
+ this.slice.axis === 'y' ? this.slice.direction : 0,
66
+ this.slice.axis === 'z' ? this.slice.direction : 0,
51
67
  ).normalize(),
52
68
  rotationIncrement,
53
69
  );
@@ -0,0 +1,18 @@
1
+ // @ts-check
2
+ /** @typedef {"exponential" | "next" | "fixed" | "match"} AnimationStyle */
3
+ export default class CubeSettings {
4
+ /**
5
+ *
6
+ * @param {number} pieceGap
7
+ * @param {number} animationSpeed
8
+ * @param {AnimationStyle} animationStyle
9
+ */
10
+ constructor(pieceGap, animationSpeed, animationStyle) {
11
+ /** @type {number} pieceGap */
12
+ this.pieceGap = pieceGap;
13
+ /** @type {number} pieceGap */
14
+ this.animationSpeedMs = animationSpeed;
15
+ /** @type {AnimationStyle} pieceGap */
16
+ this.animationStyle = animationStyle;
17
+ }
18
+ }
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  import { Group } from 'three';
2
3
  import Materials from '../threejs/materials';
3
4
  import { createCornerGroup, createEdgeGroup, createCenterGroup } from '../threejs/pieces';
@@ -0,0 +1,143 @@
1
+ // @ts-check
2
+ /** @typedef {{axis: import('../core').Axis, layers: (-1|0|1)[], direction: 1|-1|2|-2}} Slice */
3
+
4
+ import { Axi, Movements, Rotations } from '../core';
5
+
6
+ /**
7
+ * @param {import("../core").Movement} movement
8
+ * @returns {Slice}
9
+ */
10
+ export default function GetMovementSlice(movement) {
11
+ switch (movement) {
12
+ // Up
13
+ case Movements.U:
14
+ return { axis: Axi.y, layers: [1], direction: -1 };
15
+ case Movements.U2:
16
+ return { axis: Axi.y, layers: [1], direction: -2 };
17
+ case Movements.UP:
18
+ return { axis: Axi.y, layers: [1], direction: 1 };
19
+ case Movements.u:
20
+ return { axis: Axi.y, layers: [1, 0], direction: -1 };
21
+ case Movements.u2:
22
+ return { axis: Axi.y, layers: [1, 0], direction: -2 };
23
+ case Movements.uP:
24
+ return { axis: Axi.y, layers: [1, 0], direction: 1 };
25
+ // Down
26
+ case Movements.D:
27
+ return { axis: Axi.y, layers: [-1], direction: -1 };
28
+ case Movements.D2:
29
+ return { axis: Axi.y, layers: [-1], direction: -2 };
30
+ case Movements.DP:
31
+ return { axis: Axi.y, layers: [-1], direction: 1 };
32
+ case Movements.d:
33
+ return { axis: Axi.y, layers: [-1, 0], direction: -1 };
34
+ case Movements.d2:
35
+ return { axis: Axi.y, layers: [-1, 0], direction: -2 };
36
+ case Movements.dP:
37
+ return { axis: Axi.y, layers: [-1, 0], direction: 1 };
38
+ // Right
39
+ case Movements.R:
40
+ return { axis: Axi.x, layers: [1], direction: -1 };
41
+ case Movements.R2:
42
+ return { axis: Axi.x, layers: [1], direction: -2 };
43
+ case Movements.RP:
44
+ return { axis: Axi.x, layers: [1], direction: 1 };
45
+ case Movements.r:
46
+ return { axis: Axi.x, layers: [1, 0], direction: -1 };
47
+ case Movements.r2:
48
+ return { axis: Axi.x, layers: [1, 0], direction: -2 };
49
+ case Movements.rP:
50
+ return { axis: Axi.x, layers: [1, 0], direction: 1 };
51
+ // Left
52
+ case Movements.L:
53
+ return { axis: Axi.x, layers: [-1], direction: -1 };
54
+ case Movements.L2:
55
+ return { axis: Axi.x, layers: [-1], direction: -2 };
56
+ case Movements.LP:
57
+ return { axis: Axi.x, layers: [-1], direction: 1 };
58
+ case Movements.l:
59
+ return { axis: Axi.x, layers: [-1, 0], direction: -1 };
60
+ case Movements.l2:
61
+ return { axis: Axi.x, layers: [-1, 0], direction: -2 };
62
+ case Movements.lP:
63
+ return { axis: Axi.x, layers: [-1, 0], direction: 1 };
64
+ // Front
65
+ case Movements.F:
66
+ return { axis: Axi.z, layers: [1], direction: -1 };
67
+ case Movements.F2:
68
+ return { axis: Axi.z, layers: [1], direction: -2 };
69
+ case Movements.FP:
70
+ return { axis: Axi.z, layers: [1], direction: 1 };
71
+ case Movements.f:
72
+ return { axis: Axi.z, layers: [1, 0], direction: -1 };
73
+ case Movements.f2:
74
+ return { axis: Axi.z, layers: [1, 0], direction: -2 };
75
+ case Movements.fP:
76
+ return { axis: Axi.z, layers: [1, 0], direction: 1 };
77
+ // Back
78
+ case Movements.B:
79
+ return { axis: Axi.z, layers: [-1], direction: -1 };
80
+ case Movements.B2:
81
+ return { axis: Axi.z, layers: [-1], direction: -2 };
82
+ case Movements.BP:
83
+ return { axis: Axi.z, layers: [-1], direction: 1 };
84
+ case Movements.b:
85
+ return { axis: Axi.z, layers: [-1, 0], direction: -1 };
86
+ case Movements.b2:
87
+ return { axis: Axi.z, layers: [-1, 0], direction: -2 };
88
+ case Movements.bP:
89
+ return { axis: Axi.z, layers: [-1, 0], direction: 1 };
90
+ //Middle
91
+ case Movements.M:
92
+ return { axis: Axi.x, layers: [0], direction: -1 };
93
+ case Movements.M2:
94
+ return { axis: Axi.x, layers: [0], direction: -2 };
95
+ case Movements.MP:
96
+ return { axis: Axi.x, layers: [0], direction: 1 };
97
+ //Equator
98
+ case Movements.E:
99
+ return { axis: Axi.y, layers: [0], direction: -1 };
100
+ case Movements.E2:
101
+ return { axis: Axi.y, layers: [0], direction: -2 };
102
+ case Movements.EP:
103
+ return { axis: Axi.y, layers: [0], direction: 1 };
104
+ //Slice
105
+ case Movements.S:
106
+ return { axis: Axi.z, layers: [0], direction: -1 };
107
+ case Movements.S2:
108
+ return { axis: Axi.z, layers: [0], direction: -2 };
109
+ case Movements.SP:
110
+ return { axis: Axi.z, layers: [0], direction: 1 };
111
+ default:
112
+ throw Error(`Failed to get movement slice. invalid movement: ${movement}`);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @param {import("../core").Rotation} rotation
118
+ * @returns {Slice}
119
+ */
120
+ export function GetRotationSlice(rotation) {
121
+ switch (rotation) {
122
+ case Rotations.x:
123
+ return { axis: Axi.x, layers: [], direction: -1 };
124
+ case Rotations.x2:
125
+ return { axis: Axi.x, layers: [], direction: -2 };
126
+ case Rotations.xP:
127
+ return { axis: Axi.x, layers: [], direction: 1 };
128
+ case Rotations.y:
129
+ return { axis: Axi.y, layers: [], direction: -1 };
130
+ case Rotations.y2:
131
+ return { axis: Axi.y, layers: [], direction: -2 };
132
+ case Rotations.yP:
133
+ return { axis: Axi.y, layers: [], direction: 1 };
134
+ case Rotations.z:
135
+ return { axis: Axi.z, layers: [], direction: -1 };
136
+ case Rotations.z2:
137
+ return { axis: Axi.z, layers: [], direction: -2 };
138
+ case Rotations.zP:
139
+ return { axis: Axi.z, layers: [], direction: 1 };
140
+ default:
141
+ throw Error(`Failed to get rotation slice. invalid rotation: ${rotation}`);
142
+ }
143
+ }
@@ -0,0 +1,16 @@
1
+ // @ts-check
2
+ /**
3
+ * @param {number} delay
4
+ * @param {{ (entries: { contentRect: { width: number; height: number; }; }[]): void; apply?: any; }} f
5
+ */
6
+ export function debounce(f, delay) {
7
+ let timer = 0;
8
+ /**
9
+ * @this {any}
10
+ * @param {any[]} args
11
+ */
12
+ return function (...args) {
13
+ clearTimeout(timer);
14
+ timer = setTimeout(() => f.apply(this, args), delay);
15
+ };
16
+ }
package/src/globals.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { RubiksCubeElement } from '.';
2
+
3
+ declare global {
4
+ interface HTMLElementTagNameMap {
5
+ 'rubiks-cube': RubiksCubeElement;
6
+ }
7
+ }
8
+
9
+ export {};