@houstonp/rubiks-cube 1.3.0 → 1.4.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 CHANGED
@@ -38,51 +38,85 @@ import '@houstonp/rubiks-cube';
38
38
 
39
39
  ## state of the component
40
40
 
41
- A state event occurs when a movement animation is completed. The event details contains the current state of the cube. The state is an object containing the stickers of each face. A sticker is either "up", "down", "left", "right", "front" or "back".
41
+ A state event occurs when a movement animation is completed. The event details contains the current state of the cube along with the eventId of the animation. The state is an object containing the stickers of each face. A sticker is either "up", "down", "left", "right", "front" or "back".
42
42
 
43
43
  To listen for the state event, add an event listener to the rubiks-cube element.
44
44
 
45
45
  ```js
46
46
  const cube = document.querySelector('rubiks-cube');
47
47
  cube.addEventListener('state', (e) => {
48
- console.log(e.detail.state);
48
+ console.log(e.detail);
49
49
  });
50
50
  /*
51
51
  {
52
- up: [
53
- [sticker, sticker, sticker],
54
- [sticker, sticker, sticker],
55
- [sticker, sticker, sticker],
56
- ],
57
- down: [
58
- [sticker, sticker, sticker],
59
- [sticker, sticker, sticker],
60
- [sticker, sticker, sticker],
61
- ],
62
- left: [
63
- [sticker, sticker, sticker],
64
- [sticker, sticker, sticker],
65
- [sticker, sticker, sticker],
66
- ],
67
- right: [
68
- [sticker, sticker, sticker],
69
- [sticker, sticker, sticker],
70
- [sticker, sticker, sticker],
71
- ],
72
- front: [
73
- [sticker, sticker, sticker],
74
- [sticker, sticker, sticker],
75
- [sticker, sticker, sticker],
76
- ],
77
- back: [
78
- [sticker, sticker, sticker],
79
- [sticker, sticker, sticker],
80
- [sticker, sticker, sticker],
81
- ],
52
+ eventId: "guid-guid-guid-guid-guid",
53
+ state: {
54
+ up: [
55
+ [sticker, sticker, sticker],
56
+ [sticker, sticker, sticker],
57
+ [sticker, sticker, sticker],
58
+ ],
59
+ down: [
60
+ [sticker, sticker, sticker],
61
+ [sticker, sticker, sticker],
62
+ [sticker, sticker, sticker],
63
+ ],
64
+ left: [
65
+ [sticker, sticker, sticker],
66
+ [sticker, sticker, sticker],
67
+ [sticker, sticker, sticker],
68
+ ],
69
+ right: [
70
+ [sticker, sticker, sticker],
71
+ [sticker, sticker, sticker],
72
+ [sticker, sticker, sticker],
73
+ ],
74
+ front: [
75
+ [sticker, sticker, sticker],
76
+ [sticker, sticker, sticker],
77
+ [sticker, sticker, sticker],
78
+ ],
79
+ back: [
80
+ [sticker, sticker, sticker],
81
+ [sticker, sticker, sticker],
82
+ [sticker, sticker, sticker],
83
+ ],
84
+ }
82
85
  }
83
86
  */
84
87
  ```
85
88
 
89
+ ## Rubiks Cube Notation
90
+
91
+ Notations can include the number of roations of a face. For example, `U2` means rotate the upper face 180 degrees.
92
+
93
+ Noations can also include a prime symbol `'` to indicate a counter clockwise rotation. For example, `U'` means rotate the upper face counter clockwise. The direction is always determined relative to the face being moved.
94
+
95
+ When both a number and a prime symbol are included the number is stated before the prime symbol. For example, `U2'` means rotate the upper face 180 degrees counter clockwise. and `U'2` is invalid.
96
+
97
+ | Notation | Movement |
98
+ | -------- | ------------------------------------------------ |
99
+ | U | Top face clockwise |
100
+ | u | Top two layers clockwise |
101
+ | D | Bottom face clockwise |
102
+ | d | Bottom two layers clockwise |
103
+ | L | Left face clockwise |
104
+ | l | Left two layers clockwise |
105
+ | R | Right face clockwise |
106
+ | r | Right two layers clockwise |
107
+ | F | Front face clockwise |
108
+ | f | Front two layers clockwise |
109
+ | B | Back face clockwise |
110
+ | b | Back two layers clockwise |
111
+ | M | Middle layer clockwise (relative to L) |
112
+ | E | Equatorial layer clockwise (relative to D) |
113
+ | S | Standing layer clockwise (relative to F) |
114
+ | x | Rotate cube on x axis clockwise (direction of R) |
115
+ | y | Rotate cube on y axis clockwise (direction of U) |
116
+ | z | Rotate cube on z axis clockwise (direction of F) |
117
+
118
+ these symbols are used as actionIds for the action event below.
119
+
86
120
  ## Updating the component
87
121
 
88
122
  The Rubiks cube web component listens for custom events to perform twists, rotations and camera changes. As per convention, the starting rotation has green facing forward, white facing up and red facing to the right.
@@ -98,11 +132,29 @@ const cube = document.querySelector('rubiks-cube');
98
132
  cube.dispatchEvent(new CustomEvent('reset'));
99
133
  ```
100
134
 
101
- ### Camera events
135
+ ### Action event
102
136
 
103
- The rubiks-cube element listens for the `camera` custom event and moves the camera to the specified position.
137
+ The rubiks-cube element listens for the `action` custom event and moves the camera to the specified position.
138
+
139
+ ```js
140
+ {
141
+ eventId: string,
142
+ action: {
143
+ type: "movement" | "camera" | "rotation",
144
+ actionId: string
145
+ }
146
+ }
147
+ ```
148
+
149
+ ```js
150
+ var event = new CustomEvent('action', {
151
+ detail: { eventId: 'guid-guid-guid-guid-guid', action: { type: 'camera', actionId: 'peek-toggle-horizontal' } },
152
+ });
153
+ ```
104
154
 
105
- The camera position specified in the event details must be one of the following:
155
+ #### Camera action event
156
+
157
+ action IDs for camera actions are as follows
106
158
 
107
159
  - `peek-right` - Camera is moved to the right of the cube so that the right face is visible
108
160
  - `peek-left` - Camera is moved to the left of the cube so that the left face is visible
@@ -111,61 +163,94 @@ The camera position specified in the event details must be one of the following:
111
163
  - `peek-toggle-horizontal` - Camera is moved to the opposite side of the cube in the horizontal plane
112
164
  - `peek-toggle-vertical` - Camera is moved to the opposite side of the cube in the vertical plane
113
165
 
114
- Note: The camera position cannot change to perform an equivalent cube rotation.
115
-
116
166
  #### Example
117
167
 
118
168
  ```js
119
169
  const cube = document.querySelector('rubiks-cube');
120
- cube.dispatchEvent(
121
- new CustomEvent('camera', {
122
- detail: { action: 'peek-right' },
123
- }),
124
- );
170
+ const event = new CustomEvent('action', {
171
+ detail: { eventId: 'guid-guid-guid-guid-guid', action: { type: 'camera', actionId: 'peek-toggle-horizontal' } },
172
+ });
173
+ cube.dispatchEvent(event);
125
174
  ```
126
175
 
127
- ### Rotation event
176
+ #### Rotation action event
128
177
 
129
- The rubiks-cube element listens for the `rotate` custom event and rotates a face or entire cube in the direction specified by the event details.
178
+ | Notation | Rotation |
179
+ | -------- | ------------------------------------------------ |
180
+ | x | Rotate cube on x axis clockwise (direction of R) |
181
+ | y | Rotate cube on y axis clockwise (direction of U) |
182
+ | z | Rotate cube on z axis clockwise (direction of F) |
130
183
 
131
- The rotation type specified in the event details must follow standard rubiks cube notation.
184
+ actionIDs for action type "rotation" are as follows
132
185
 
133
- #### Rubiks Cube Notation
186
+ - 'x',
187
+ - 'x2',
188
+ - "x'",
189
+ - 'y',
190
+ - 'y2',
191
+ - "y'",
192
+ - 'z',
193
+ - 'z2',
194
+ - "z'",
134
195
 
135
- Notations can include the number of roations of a face. For example, `U2` means rotate the upper face 180 degrees.
196
+ #### Example
136
197
 
137
- Noations can also include a prime symbol `'` to indicate a counter clockwise rotation. For example, `U'` means rotate the upper face counter clockwise. The direction is always determined relative to the face being moved.
198
+ ```js
199
+ const cube = document.querySelector('rubiks-cube');
200
+ const event = new CustomEvent('action', {
201
+ detail: { eventId: 'guid-guid-guid-guid-guid', action: { type: 'rotation', actionId: 'x' } },
202
+ });
203
+ cube.dispatchEvent(event);
204
+ ```
138
205
 
139
- When both a number and a prime symbol are included the number is stated before the prime symbol. For example, `U2'` means rotate the upper face 180 degrees counter clockwise. and `U'2` is invalid.
140
-
141
- | Notation | Movement |
142
- | -------- | ------------------------------------------------ |
143
- | U | Top face clockwise |
144
- | u | Top two layers clockwise |
145
- | D | Bottom face clockwise |
146
- | d | Bottom two layers clockwise |
147
- | L | Left face clockwise |
148
- | l | Left two layers clockwise |
149
- | R | Right face clockwise |
150
- | r | Right two layers clockwise |
151
- | F | Front face clockwise |
152
- | f | Front two layers clockwise |
153
- | B | Back face clockwise |
154
- | b | Back two layers clockwise |
155
- | M | Middle layer clockwise (relative to L) |
156
- | E | Equatorial layer clockwise (relative to D) |
157
- | S | Standing layer clockwise (relative to F) |
158
- | x | Rotate cube on x axis clockwise (direction of R) |
159
- | y | Rotate cube on y axis clockwise (direction of U) |
160
- | z | Rotate cube on z axis clockwise (direction of F) |
206
+ #### Movement action event
207
+
208
+ | Notation | Movement |
209
+ | -------- | ------------------------------------------ |
210
+ | U | Top face clockwise |
211
+ | u | Top two layers clockwise |
212
+ | D | Bottom face clockwise |
213
+ | d | Bottom two layers clockwise |
214
+ | L | Left face clockwise |
215
+ | l | Left two layers clockwise |
216
+ | R | Right face clockwise |
217
+ | r | Right two layers clockwise |
218
+ | F | Front face clockwise |
219
+ | f | Front two layers clockwise |
220
+ | B | Back face clockwise |
221
+ | b | Back two layers clockwise |
222
+ | M | Middle layer clockwise (relative to L) |
223
+ | E | Equatorial layer clockwise (relative to D) |
224
+ | S | Standing layer clockwise (relative to F) |
225
+
226
+ actionIDs for action type "movement" are as follows
227
+
228
+ - 'R',
229
+ - 'R2',
230
+ - "R'",
231
+ - 'L',
232
+ - 'L2',
233
+ - "L'",
234
+ - 'U',
235
+ - 'U2',
236
+ - "U'",
237
+ - 'D',
238
+ - 'D2',
239
+ - "D'",
240
+ - 'F',
241
+ - 'F2',
242
+ - "F'",
243
+ - 'B',
244
+ - 'B2',
245
+ - "B'",
246
+ - etc...
161
247
 
162
248
  #### Example
163
249
 
164
250
  ```js
165
251
  const cube = document.querySelector('rubiks-cube');
166
- cube.dispatchEvent(
167
- new CustomEvent('rotate', {
168
- detail: { action: "u2'" },
169
- }),
170
- );
252
+ const event = new CustomEvent('action', {
253
+ detail: { eventId: 'guid-guid-guid-guid-guid', action: { type: 'movement', actionId: 'U' } },
254
+ });
255
+ cube.dispatchEvent(event);
171
256
  ```
package/index.js CHANGED
@@ -16,8 +16,6 @@ class RubiksCube extends HTMLElement {
16
16
  super();
17
17
  /** @type {number} */
18
18
  this.animationSpeed = defaultAnimationSpeed;
19
- /** @type {"exponential" | "instant"} */
20
- this.animationStyle = this.getAttribute('animation-style') || defaultAnimationStyle;
21
19
  this.attachShadow({ mode: 'open' });
22
20
  this.shadowRoot.innerHTML = `<canvas id="cube-canvas" style="display:block;"></canvas>`;
23
21
  this.canvas = this.shadowRoot.getElementById('cube-canvas');
@@ -116,9 +114,8 @@ class RubiksCube extends HTMLElement {
116
114
  const cameraAnimationGroup = new Group();
117
115
  cameraAnimationGroup.add(new Tween(camera.position).to({ x: 2.5, y: 2.5, z: 4 }, 1000).easing(Easing.Cubic.InOut).start());
118
116
 
119
- const sendState = () => {
120
- const state = cube.getStickerState();
121
- const event = new CustomEvent('state', { detail: { state } });
117
+ const sendState = (eventId) => {
118
+ const event = new CustomEvent('state', { detail: { eventId, state: cube.currentState } });
122
119
  this.dispatchEvent(event);
123
120
  };
124
121
 
@@ -127,9 +124,9 @@ class RubiksCube extends HTMLElement {
127
124
  cameraAnimationGroup.update();
128
125
  controls.update();
129
126
 
130
- var cubeState = cube.update();
131
- if (cubeState) {
132
- sendState();
127
+ var eventId = cube.update();
128
+ if (eventId) {
129
+ sendState(eventId);
133
130
  }
134
131
  renderer.render(scene, camera);
135
132
  }
@@ -137,28 +134,49 @@ class RubiksCube extends HTMLElement {
137
134
  // add event listeners for rotation and camera controls
138
135
  this.addEventListener('reset', () => {
139
136
  cube.reset();
140
- sendState();
137
+ sendState('reset');
141
138
  });
142
139
 
143
- this.addEventListener('rotate', (e) => {
144
- const action = getRotationDetailsFromNotation(e.detail.action);
145
- if (action !== undefined) {
146
- cube.rotate(action);
140
+ this.addEventListener('action', (e) => {
141
+ /** @type {{eventId: string, action: {type: "movement" | "camera" | "rotation", actionId: string }}} move */
142
+ var move = e.detail.move;
143
+ console.log(move);
144
+ if (move.action.type === 'camera') {
145
+ handleCameraAction(move.action.actionId);
146
+ return;
147
+ }
148
+ if (move.action.type === 'movement' || move.action.type === 'rotation') {
149
+ handleRotationAction(move.eventId, move.action.actionId);
150
+ return;
147
151
  }
148
152
  });
149
153
 
150
- this.addEventListener('camera', (e) => {
151
- if (e.detail.action === 'peek-toggle-horizontal') {
154
+ /**
155
+ * @param {string} eventId
156
+ * @param {string} actionId
157
+ */
158
+ const handleRotationAction = (eventId, actionId) => {
159
+ const rotationDetails = getRotationDetailsFromNotation(actionId);
160
+ if (rotationDetails !== undefined) {
161
+ cube.rotate(eventId, rotationDetails);
162
+ }
163
+ };
164
+
165
+ /**
166
+ * @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down'} actionId
167
+ */
168
+ const handleCameraAction = (actionId) => {
169
+ if (actionId === 'peek-toggle-horizontal') {
152
170
  cameraState.Right = !cameraState.Right;
153
- } else if (e.detail.action === 'peek-toggle-vertical') {
171
+ } else if (actionId === 'peek-toggle-vertical') {
154
172
  cameraState.Up = !cameraState.Up;
155
- } else if (e.detail.action === 'peek-right') {
173
+ } else if (actionId === 'peek-right') {
156
174
  cameraState.Right = true;
157
- } else if (e.detail.action === 'peek-left') {
175
+ } else if (actionId === 'peek-left') {
158
176
  cameraState.Right = false;
159
- } else if (e.detail.action === 'peek-up') {
177
+ } else if (actionId === 'peek-up') {
160
178
  cameraState.Up = true;
161
- } else if (e.detail.action === 'peek-down') {
179
+ } else if (actionId === 'peek-down') {
162
180
  cameraState.Up = false;
163
181
  }
164
182
  cameraAnimationGroup.add(
@@ -173,7 +191,7 @@ class RubiksCube extends HTMLElement {
173
191
  )
174
192
  .start(),
175
193
  );
176
- });
194
+ };
177
195
  }
178
196
  }
179
197
  customElements.define('rubiks-cube', RubiksCube);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@houstonp/rubiks-cube",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Rubiks Cube Web Component built with threejs",
5
5
  "main": "index.js",
6
6
  "author": "Houston Pearse",
package/src/cube/cube.js CHANGED
@@ -18,6 +18,8 @@ export default class Cube {
18
18
  this.rotationQueue = [];
19
19
  /** @type {CubeRotation | undefined} */
20
20
  this.currentRotation = undefined;
21
+ /** @type {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] }} */
22
+ this.currentState = this.getStickerState();
21
23
  /** @type {number | undefined} */
22
24
  this._matchSpeed = undefined;
23
25
  /** @type {number} */
@@ -82,8 +84,10 @@ export default class Cube {
82
84
  }
83
85
  if (this.currentRotation.status === 'complete') {
84
86
  this.clearRotationGroup();
87
+ var eventId = this.currentRotation.eventId;
85
88
  this.currentRotation = undefined;
86
- return this.getStickerState();
89
+ this.currentState = this.getStickerState();
90
+ return eventId;
87
91
  }
88
92
  return undefined;
89
93
  }
@@ -194,13 +198,11 @@ export default class Cube {
194
198
  }
195
199
 
196
200
  /**
201
+ * @param {string} eventId
197
202
  * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} input
198
203
  */
199
- rotate(input) {
200
- var queueLength = this.rotationQueue.length;
201
- if (queueLength > 0 && this.rotationQueue[queueLength - 1].rotation.axis === input.axis) {
202
- }
203
- this.rotationQueue.push(new CubeRotation(input));
204
+ rotate(eventId, input) {
205
+ this.rotationQueue.push(new CubeRotation(eventId, input));
204
206
  }
205
207
 
206
208
  /**
@@ -2,11 +2,14 @@ import { Vector3, Group } from 'three';
2
2
 
3
3
  export class CubeRotation {
4
4
  /**
5
- * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} input
5
+ * @param {string} eventId
6
+ * @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} rotationDetails
6
7
  */
7
- constructor(rotation) {
8
+ constructor(eventId, rotationDetails) {
9
+ /** @type {string} */
10
+ this.eventId = eventId;
8
11
  /** @type {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} */
9
- this.rotation = rotation;
12
+ this.rotation = rotationDetails;
10
13
  /** @type {"pending" | "initialised" | "complete" | "disposed"} */
11
14
  this.status = 'pending';
12
15
  /** @type {number} */