@houstonp/rubiks-cube 1.3.0 → 1.4.1
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 +160 -75
- package/index.js +38 -21
- package/package.json +1 -1
- package/src/cube/cube.js +8 -6
- package/src/cube/cubeRotation.js +6 -3
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
|
|
48
|
+
console.log(e.detail);
|
|
49
49
|
});
|
|
50
50
|
/*
|
|
51
51
|
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
[
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
[
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
[
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
[
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
[
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[
|
|
80
|
-
|
|
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
|
-
###
|
|
135
|
+
### Action event
|
|
102
136
|
|
|
103
|
-
The rubiks-cube element listens for the `
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
176
|
+
#### Rotation action event
|
|
128
177
|
|
|
129
|
-
|
|
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
|
-
|
|
184
|
+
actionIDs for action type "rotation" are as follows
|
|
132
185
|
|
|
133
|
-
|
|
186
|
+
- 'x',
|
|
187
|
+
- 'x2',
|
|
188
|
+
- "x'",
|
|
189
|
+
- 'y',
|
|
190
|
+
- 'y2',
|
|
191
|
+
- "y'",
|
|
192
|
+
- 'z',
|
|
193
|
+
- 'z2',
|
|
194
|
+
- "z'",
|
|
134
195
|
|
|
135
|
-
|
|
196
|
+
#### Example
|
|
136
197
|
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
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
|
|
131
|
-
if (
|
|
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,48 @@ 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('
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
this.addEventListener('action', (e) => {
|
|
141
|
+
/** @type {{eventId: string, action: {type: "movement" | "camera" | "rotation", actionId: string }}} move */
|
|
142
|
+
var move = e.detail.move;
|
|
143
|
+
if (move.action.type === 'camera') {
|
|
144
|
+
handleCameraAction(move.action.actionId);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (move.action.type === 'movement' || move.action.type === 'rotation') {
|
|
148
|
+
handleRotationAction(move.eventId, move.action.actionId);
|
|
149
|
+
return;
|
|
147
150
|
}
|
|
148
151
|
});
|
|
149
152
|
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
/**
|
|
154
|
+
* @param {string} eventId
|
|
155
|
+
* @param {string} actionId
|
|
156
|
+
*/
|
|
157
|
+
const handleRotationAction = (eventId, actionId) => {
|
|
158
|
+
const rotationDetails = getRotationDetailsFromNotation(actionId);
|
|
159
|
+
if (rotationDetails !== undefined) {
|
|
160
|
+
cube.rotate(eventId, rotationDetails);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down'} actionId
|
|
166
|
+
*/
|
|
167
|
+
const handleCameraAction = (actionId) => {
|
|
168
|
+
if (actionId === 'peek-toggle-horizontal') {
|
|
152
169
|
cameraState.Right = !cameraState.Right;
|
|
153
|
-
} else if (
|
|
170
|
+
} else if (actionId === 'peek-toggle-vertical') {
|
|
154
171
|
cameraState.Up = !cameraState.Up;
|
|
155
|
-
} else if (
|
|
172
|
+
} else if (actionId === 'peek-right') {
|
|
156
173
|
cameraState.Right = true;
|
|
157
|
-
} else if (
|
|
174
|
+
} else if (actionId === 'peek-left') {
|
|
158
175
|
cameraState.Right = false;
|
|
159
|
-
} else if (
|
|
176
|
+
} else if (actionId === 'peek-up') {
|
|
160
177
|
cameraState.Up = true;
|
|
161
|
-
} else if (
|
|
178
|
+
} else if (actionId === 'peek-down') {
|
|
162
179
|
cameraState.Up = false;
|
|
163
180
|
}
|
|
164
181
|
cameraAnimationGroup.add(
|
|
@@ -173,7 +190,7 @@ class RubiksCube extends HTMLElement {
|
|
|
173
190
|
)
|
|
174
191
|
.start(),
|
|
175
192
|
);
|
|
176
|
-
}
|
|
193
|
+
};
|
|
177
194
|
}
|
|
178
195
|
}
|
|
179
196
|
customElements.define('rubiks-cube', RubiksCube);
|
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/src/cube/cubeRotation.js
CHANGED
|
@@ -2,11 +2,14 @@ import { Vector3, Group } from 'three';
|
|
|
2
2
|
|
|
3
3
|
export class CubeRotation {
|
|
4
4
|
/**
|
|
5
|
-
* @param {
|
|
5
|
+
* @param {string} eventId
|
|
6
|
+
* @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} rotationDetails
|
|
6
7
|
*/
|
|
7
|
-
constructor(
|
|
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 =
|
|
12
|
+
this.rotation = rotationDetails;
|
|
10
13
|
/** @type {"pending" | "initialised" | "complete" | "disposed"} */
|
|
11
14
|
this.status = 'pending';
|
|
12
15
|
/** @type {number} */
|