@houstonp/rubiks-cube 1.2.1 → 1.3.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 +9 -6
- package/index.js +46 -84
- package/package.json +1 -1
- package/src/cube/cube.js +104 -28
- package/src/cube/cubeRotation.js +7 -7
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This package is a rubiks cube web component built with threejs. Camera animation smoothing is done with the tweenjs package.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
## Adding the component
|
|
6
8
|
|
|
7
9
|
You can add the component to a webpage by adding an import statement in the index.js file. And then
|
|
@@ -19,7 +21,7 @@ import '@houstonp/rubiks-cube';
|
|
|
19
21
|
<meta charset="utf-8" />
|
|
20
22
|
</head>
|
|
21
23
|
<body>
|
|
22
|
-
<rubiks-cube animation-speed="1000" animation-style="exponential" gap="1.04"> </rubiks-cube>
|
|
24
|
+
<rubiks-cube animation-speed="1000" animation-style="exponential" piece-gap="1.04" camera-speed="100"> </rubiks-cube>
|
|
23
25
|
<script type="module" src="index.js"></script>
|
|
24
26
|
</body>
|
|
25
27
|
</html>
|
|
@@ -27,11 +29,12 @@ import '@houstonp/rubiks-cube';
|
|
|
27
29
|
|
|
28
30
|
## component attributes
|
|
29
31
|
|
|
30
|
-
| attribute | accepted values
|
|
31
|
-
| --------------- |
|
|
32
|
-
| animation-speed | integer greater than 0
|
|
33
|
-
| animation-style | "exponetial", "next", "fixed" | fixed: fixed animation lengths, next: skips to next animation, exponential: speeds up successive animations |
|
|
34
|
-
| gap
|
|
32
|
+
| attribute | accepted values | Description |
|
|
33
|
+
| --------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| animation-speed | integer greater than or equal to 0 | sets the speed of the animations in milliseconds |
|
|
35
|
+
| animation-style | "exponetial", "next", "fixed", "match" | fixed: fixed animation lengths, next: skips to next animation, exponential: speeds up successive animations, match: matches the speed the frequency of events |
|
|
36
|
+
| piece-gap | greater than 1 | sets the gap between rubiks cube pieces |
|
|
37
|
+
| camera-speed | greater than or equal to 0 | sets the speed of camera animations in milliseconds |
|
|
35
38
|
|
|
36
39
|
## state of the component
|
|
37
40
|
|
package/index.js
CHANGED
|
@@ -6,8 +6,10 @@ import getRotationDetailsFromNotation from './src/utils/rotation';
|
|
|
6
6
|
import { debounce } from './src/utils/debouncer';
|
|
7
7
|
|
|
8
8
|
const defaultAnimationSpeed = 100;
|
|
9
|
-
const
|
|
9
|
+
const defaultCameraSpeed = 100;
|
|
10
|
+
const defaultAnimationStyle = 'fixed';
|
|
10
11
|
const defaultGap = 1.04;
|
|
12
|
+
const minimumGap = 1;
|
|
11
13
|
|
|
12
14
|
class RubiksCube extends HTMLElement {
|
|
13
15
|
constructor() {
|
|
@@ -19,27 +21,34 @@ class RubiksCube extends HTMLElement {
|
|
|
19
21
|
this.attachShadow({ mode: 'open' });
|
|
20
22
|
this.shadowRoot.innerHTML = `<canvas id="cube-canvas" style="display:block;"></canvas>`;
|
|
21
23
|
this.canvas = this.shadowRoot.getElementById('cube-canvas');
|
|
22
|
-
/** @type {{
|
|
24
|
+
/** @type {{animationStyle: "exponential" | "next" | "fixed" | "match", animationSpeed: number, gap: number, cameraSpeed: number}} */
|
|
23
25
|
this.settings = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
gap: this.getAttribute('gap') || defaultGap,
|
|
26
|
+
animationSpeed: this.getAttribute('animation-speed') || defaultAnimationSpeed,
|
|
27
|
+
animationStyle: this.getAttribute('animation-style') || defaultAnimationStyle,
|
|
28
|
+
gap: this.getAttribute('piece-gap') || defaultGap,
|
|
29
|
+
cameraSpeed: this.getAttribute('camera-speed') || defaultCameraSpeed,
|
|
27
30
|
};
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
static get observedAttributes() {
|
|
31
|
-
return ['animation-style', 'animation-speed'];
|
|
34
|
+
return ['animation-style', 'animation-speed', 'piece-gap', 'camera-speed'];
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
35
38
|
if (name === 'animation-style') {
|
|
36
|
-
this.settings.
|
|
39
|
+
this.settings.animationStyle = newVal;
|
|
37
40
|
}
|
|
38
41
|
if (name === 'animation-speed') {
|
|
39
|
-
|
|
42
|
+
var speed = Number(newVal);
|
|
43
|
+
this.settings.animationSpeed = speed > 0 ? speed : 0;
|
|
40
44
|
}
|
|
41
|
-
if (name === 'gap') {
|
|
42
|
-
|
|
45
|
+
if (name === 'piece-gap') {
|
|
46
|
+
var gap = Number(newVal);
|
|
47
|
+
this.settings.gap = gap < minimumGap ? minimumGap : gap;
|
|
48
|
+
}
|
|
49
|
+
if (name === 'camera-speed') {
|
|
50
|
+
var speed = Number(newVal);
|
|
51
|
+
this.settings.cameraSpeed = speed > 0 ? speed : 0;
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
connectedCallback() {
|
|
@@ -71,6 +80,8 @@ class RubiksCube extends HTMLElement {
|
|
|
71
80
|
|
|
72
81
|
// add camera
|
|
73
82
|
const camera = new PerspectiveCamera(75, this.clientWidth / this.clientHeight, 0.1, 1000);
|
|
83
|
+
/** @type {{Up: boolean, Right: boolean, UpDistance: number, RightDistance: number}} */
|
|
84
|
+
const cameraState = { Up: true, Right: true, UpDistance: 2.5, RightDistance: 2.5 };
|
|
74
85
|
camera.position.z = 4;
|
|
75
86
|
camera.position.y = 3;
|
|
76
87
|
camera.position.x = 0;
|
|
@@ -115,102 +126,53 @@ class RubiksCube extends HTMLElement {
|
|
|
115
126
|
function animate() {
|
|
116
127
|
cameraAnimationGroup.update();
|
|
117
128
|
controls.update();
|
|
118
|
-
|
|
129
|
+
|
|
130
|
+
var cubeState = cube.update();
|
|
131
|
+
if (cubeState) {
|
|
132
|
+
sendState();
|
|
133
|
+
}
|
|
119
134
|
renderer.render(scene, camera);
|
|
120
135
|
}
|
|
121
136
|
|
|
137
|
+
// add event listeners for rotation and camera controls
|
|
122
138
|
this.addEventListener('reset', () => {
|
|
123
139
|
cube.reset();
|
|
124
140
|
sendState();
|
|
125
141
|
});
|
|
126
142
|
|
|
127
|
-
// add event listeners for rotation and camera controls
|
|
128
143
|
this.addEventListener('rotate', (e) => {
|
|
129
144
|
const action = getRotationDetailsFromNotation(e.detail.action);
|
|
130
145
|
if (action !== undefined) {
|
|
131
146
|
cube.rotate(action);
|
|
132
147
|
}
|
|
133
148
|
});
|
|
149
|
+
|
|
134
150
|
this.addEventListener('camera', (e) => {
|
|
135
151
|
if (e.detail.action === 'peek-toggle-horizontal') {
|
|
136
|
-
|
|
137
|
-
new Tween(camera.position)
|
|
138
|
-
.to(
|
|
139
|
-
{
|
|
140
|
-
x: camera.position.x > 0 ? -2.5 : 2.5,
|
|
141
|
-
y: camera.position.y > 0 ? 2.5 : -2.5,
|
|
142
|
-
z: 4,
|
|
143
|
-
},
|
|
144
|
-
200,
|
|
145
|
-
)
|
|
146
|
-
.start(),
|
|
147
|
-
);
|
|
152
|
+
cameraState.Right = !cameraState.Right;
|
|
148
153
|
} else if (e.detail.action === 'peek-toggle-vertical') {
|
|
149
|
-
|
|
150
|
-
new Tween(camera.position)
|
|
151
|
-
.to(
|
|
152
|
-
{
|
|
153
|
-
x: camera.position.x > 0 ? 2.5 : -2.5,
|
|
154
|
-
y: camera.position.y > 0 ? -2.5 : 2.5,
|
|
155
|
-
z: 4,
|
|
156
|
-
},
|
|
157
|
-
200,
|
|
158
|
-
)
|
|
159
|
-
.start(),
|
|
160
|
-
);
|
|
154
|
+
cameraState.Up = !cameraState.Up;
|
|
161
155
|
} else if (e.detail.action === 'peek-right') {
|
|
162
|
-
|
|
163
|
-
new Tween(camera.position)
|
|
164
|
-
.to(
|
|
165
|
-
{
|
|
166
|
-
x: 2.5,
|
|
167
|
-
y: camera.position.y > 0 ? 2.5 : -2.5,
|
|
168
|
-
z: 4,
|
|
169
|
-
},
|
|
170
|
-
200,
|
|
171
|
-
)
|
|
172
|
-
.start(),
|
|
173
|
-
);
|
|
156
|
+
cameraState.Right = true;
|
|
174
157
|
} else if (e.detail.action === 'peek-left') {
|
|
175
|
-
|
|
176
|
-
new Tween(camera.position)
|
|
177
|
-
.to(
|
|
178
|
-
{
|
|
179
|
-
x: -2.5,
|
|
180
|
-
y: camera.position.y > 0 ? 2.5 : -2.5,
|
|
181
|
-
z: 4,
|
|
182
|
-
},
|
|
183
|
-
200,
|
|
184
|
-
)
|
|
185
|
-
.start(),
|
|
186
|
-
);
|
|
158
|
+
cameraState.Right = false;
|
|
187
159
|
} else if (e.detail.action === 'peek-up') {
|
|
188
|
-
|
|
189
|
-
new Tween(camera.position)
|
|
190
|
-
.to(
|
|
191
|
-
{
|
|
192
|
-
x: camera.position.x > 0 ? 2.5 : -2.5,
|
|
193
|
-
y: 2.5,
|
|
194
|
-
z: 4,
|
|
195
|
-
},
|
|
196
|
-
200,
|
|
197
|
-
)
|
|
198
|
-
.start(),
|
|
199
|
-
);
|
|
160
|
+
cameraState.Up = true;
|
|
200
161
|
} else if (e.detail.action === 'peek-down') {
|
|
201
|
-
|
|
202
|
-
new Tween(camera.position)
|
|
203
|
-
.to(
|
|
204
|
-
{
|
|
205
|
-
x: camera.position.x > 0 ? 2.5 : -2.5,
|
|
206
|
-
y: -2.5,
|
|
207
|
-
z: 4,
|
|
208
|
-
},
|
|
209
|
-
200,
|
|
210
|
-
)
|
|
211
|
-
.start(),
|
|
212
|
-
);
|
|
162
|
+
cameraState.Up = false;
|
|
213
163
|
}
|
|
164
|
+
cameraAnimationGroup.add(
|
|
165
|
+
new Tween(camera.position)
|
|
166
|
+
.to(
|
|
167
|
+
{
|
|
168
|
+
x: cameraState.Right ? cameraState.RightDistance : -cameraState.RightDistance,
|
|
169
|
+
y: cameraState.Up ? cameraState.UpDistance : -cameraState.UpDistance,
|
|
170
|
+
z: 4,
|
|
171
|
+
},
|
|
172
|
+
this.settings.cameraSpeed,
|
|
173
|
+
)
|
|
174
|
+
.start(),
|
|
175
|
+
);
|
|
214
176
|
});
|
|
215
177
|
}
|
|
216
178
|
}
|
package/package.json
CHANGED
package/src/cube/cube.js
CHANGED
|
@@ -3,28 +3,34 @@ import { createCoreMesh } from '../threejs/pieces';
|
|
|
3
3
|
import { createCubeState } from './cubeState';
|
|
4
4
|
import { CubeRotation } from './cubeRotation';
|
|
5
5
|
|
|
6
|
-
const minimumGap = 1;
|
|
7
|
-
|
|
8
6
|
export default class Cube {
|
|
9
7
|
/**
|
|
10
|
-
* @param {{style: "exponential" | "next" | "fixed", speed: number, gap: number}}
|
|
8
|
+
* @param {{style: "exponential" | "next" | "fixed", speed: number, gap: number}} settings
|
|
11
9
|
*/
|
|
12
|
-
constructor(
|
|
13
|
-
/** @type {number} */
|
|
14
|
-
this.
|
|
10
|
+
constructor(settings) {
|
|
11
|
+
/** @type {{animationStyle: "match" | "exponential" | "next" | "fixed", animationSpeed: number, gap: number}} */
|
|
12
|
+
this.settings = settings;
|
|
15
13
|
/** @type {Group} */
|
|
16
|
-
this.group =
|
|
14
|
+
this.group = this.createCubeGroup();
|
|
17
15
|
/** @type {Group} */
|
|
18
16
|
this.rotationGroup = new Group();
|
|
19
17
|
/** @type {CubeRotation[]} */
|
|
20
18
|
this.rotationQueue = [];
|
|
21
19
|
/** @type {CubeRotation | undefined} */
|
|
22
20
|
this.currentRotation = undefined;
|
|
21
|
+
/** @type {number | undefined} */
|
|
22
|
+
this._matchSpeed = undefined;
|
|
23
23
|
/** @type {number} */
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
this.animationStyle = style;
|
|
24
|
+
this._lastGap = settings.gap;
|
|
25
|
+
}
|
|
27
26
|
|
|
27
|
+
/**
|
|
28
|
+
* creates a ThreeJS group with all the required pieces for a cube
|
|
29
|
+
* @param {Group} group
|
|
30
|
+
* @returns {Group}
|
|
31
|
+
*/
|
|
32
|
+
createCubeGroup(group) {
|
|
33
|
+
var group = new Group();
|
|
28
34
|
const core = createCoreMesh();
|
|
29
35
|
core.userData = {
|
|
30
36
|
position: { x: 0, y: 0, z: 0 },
|
|
@@ -33,11 +39,11 @@ export default class Cube {
|
|
|
33
39
|
initialRotation: { x: 0, y: 0, z: 0 },
|
|
34
40
|
type: 'core',
|
|
35
41
|
};
|
|
36
|
-
|
|
42
|
+
group.add(core);
|
|
37
43
|
|
|
38
44
|
for (const piece of createCubeState()) {
|
|
39
45
|
var pieceGroup = piece.group;
|
|
40
|
-
pieceGroup.position.set(piece.position.x * this.gap, piece.position.y * this.gap, piece.position.z * this.gap);
|
|
46
|
+
pieceGroup.position.set(piece.position.x * this.settings.gap, piece.position.y * this.settings.gap, piece.position.z * this.settings.gap);
|
|
41
47
|
pieceGroup.rotation.set(piece.rotation.x, piece.rotation.y, piece.rotation.z);
|
|
42
48
|
pieceGroup.userData = {
|
|
43
49
|
position: Object.assign({}, piece.position),
|
|
@@ -46,52 +52,111 @@ export default class Cube {
|
|
|
46
52
|
initialRotation: Object.assign({}, piece.rotation),
|
|
47
53
|
type: piece.type,
|
|
48
54
|
};
|
|
49
|
-
|
|
55
|
+
group.add(pieceGroup);
|
|
50
56
|
}
|
|
57
|
+
return group;
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
/**
|
|
61
|
+
* update the cube and continue any rotations
|
|
62
|
+
* @returns {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] } | undefined }
|
|
63
|
+
*/
|
|
53
64
|
update() {
|
|
54
65
|
if (this.currentRotation === undefined) {
|
|
66
|
+
if (this._lastGap !== this.settings.gap) {
|
|
67
|
+
this.updateGap();
|
|
68
|
+
}
|
|
55
69
|
this.currentRotation = this.rotationQueue.shift();
|
|
56
|
-
if (this.currentRotation === undefined)
|
|
70
|
+
if (this.currentRotation === undefined) {
|
|
71
|
+
this._matchSpeed = undefined; // reset speed for the match animation options
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (this.currentRotation.status === 'pending') {
|
|
57
76
|
this.rotationGroup.add(...this.getRotationLayer(this.currentRotation.rotation));
|
|
58
77
|
this.currentRotation.initialise();
|
|
59
78
|
}
|
|
60
|
-
|
|
79
|
+
if (this.currentRotation.status === 'initialised') {
|
|
80
|
+
var speed = this.getRotationSpeed();
|
|
81
|
+
this.currentRotation.update(this.rotationGroup, speed);
|
|
82
|
+
}
|
|
61
83
|
if (this.currentRotation.status === 'complete') {
|
|
62
84
|
this.clearRotationGroup();
|
|
63
|
-
this.currentRotation
|
|
64
|
-
|
|
65
|
-
if (this.currentRotation === undefined) return;
|
|
66
|
-
this.rotationGroup.add(...this.getRotationLayer(this.currentRotation.rotation));
|
|
67
|
-
this.currentRotation.initialise();
|
|
85
|
+
this.currentRotation = undefined;
|
|
86
|
+
return this.getStickerState();
|
|
68
87
|
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
69
90
|
|
|
70
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Updates the gap of the pieces. To be used when the cube is not rotating
|
|
93
|
+
* @returns {void}
|
|
94
|
+
*/
|
|
95
|
+
updateGap() {
|
|
96
|
+
if (this.currentRotation === undefined) {
|
|
97
|
+
this.group.children.forEach((piece) => {
|
|
98
|
+
var { x, y, z } = piece.userData.position;
|
|
99
|
+
piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
|
|
100
|
+
});
|
|
101
|
+
this._lastGap = this.settings.gap;
|
|
102
|
+
}
|
|
71
103
|
}
|
|
72
104
|
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* calculates the current speed of the current rotation in ms.
|
|
108
|
+
* calculation is dependent on animation style and animation speed settings
|
|
109
|
+
* - exponential: speeds up rotations depending on the queue length
|
|
110
|
+
* - next: an animation speed of 0 when there is another animation in the queue
|
|
111
|
+
* - match: will match the speed of rotations to the frequency of key presses.
|
|
112
|
+
* - fixed: will return a constant value
|
|
113
|
+
* @returns {number}
|
|
114
|
+
*/
|
|
73
115
|
getRotationSpeed() {
|
|
74
|
-
if (this.animationStyle
|
|
75
|
-
return this.animationSpeed / 2 ** this.rotationQueue.length;
|
|
116
|
+
if (this.settings.animationStyle === 'exponential') {
|
|
117
|
+
return this.settings.animationSpeed / 2 ** this.rotationQueue.length;
|
|
118
|
+
}
|
|
119
|
+
if (this.settings.animationStyle === 'next') {
|
|
120
|
+
return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeed;
|
|
121
|
+
}
|
|
122
|
+
if (this.settings.animationStyle === 'match') {
|
|
123
|
+
if (this.rotationQueue.length > 0) {
|
|
124
|
+
var lastTimeStamp = this.currentRotation.timestampMs;
|
|
125
|
+
var minGap = this._matchSpeed ?? this.settings.animationSpeed;
|
|
126
|
+
for (var i = 0; i < this.rotationQueue.length; i++) {
|
|
127
|
+
var gap = this.rotationQueue[i].timestampMs - lastTimeStamp;
|
|
128
|
+
if (gap < minGap) {
|
|
129
|
+
minGap = gap;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
this._matchSpeed = minGap;
|
|
133
|
+
}
|
|
134
|
+
if (this._matchSpeed !== undefined) {
|
|
135
|
+
return this._matchSpeed;
|
|
136
|
+
}
|
|
137
|
+
return this.settings.animationSpeed;
|
|
76
138
|
}
|
|
77
|
-
if (this.animationStyle
|
|
78
|
-
return this.
|
|
139
|
+
if (this.settings.animationStyle === 'fixed') {
|
|
140
|
+
return this.settings.animationSpeed;
|
|
79
141
|
}
|
|
80
|
-
return this.animationSpeed;
|
|
142
|
+
return this.settings.animationSpeed;
|
|
81
143
|
}
|
|
82
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Complete the current rotation and reset the cube
|
|
147
|
+
* @returns {void}
|
|
148
|
+
*/
|
|
83
149
|
reset() {
|
|
84
150
|
this.rotationQueue = [];
|
|
85
151
|
if (this.currentRotation) {
|
|
86
152
|
this.currentRotation.update(this.rotationGroup, 0);
|
|
87
153
|
this.clearRotationGroup();
|
|
88
|
-
this.currentRotation.dispose();
|
|
89
154
|
this.currentRotation = undefined;
|
|
90
155
|
}
|
|
91
156
|
this.group.children.forEach((piece) => {
|
|
92
157
|
const { x, y, z } = piece.userData.initialPosition;
|
|
93
158
|
const { x: u, y: v, z: w } = piece.userData.initialRotation;
|
|
94
|
-
piece.position.set(x * this.gap, y * this.gap, z * this.gap);
|
|
159
|
+
piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
|
|
95
160
|
piece.rotation.set(u, v, w);
|
|
96
161
|
piece.userData.position.x = x;
|
|
97
162
|
piece.userData.position.y = y;
|
|
@@ -102,6 +167,10 @@ export default class Cube {
|
|
|
102
167
|
});
|
|
103
168
|
}
|
|
104
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Adds pieces in the rotationGroup back into the main group.
|
|
172
|
+
* @returns {void}
|
|
173
|
+
*/
|
|
105
174
|
clearRotationGroup() {
|
|
106
175
|
if (this.currentRotation.status != 'complete') {
|
|
107
176
|
throw Error('cannot clear rotation group while rotating');
|
|
@@ -121,12 +190,16 @@ export default class Cube {
|
|
|
121
190
|
});
|
|
122
191
|
this.group.add(...this.rotationGroup.children);
|
|
123
192
|
this.rotationGroup.rotation.set(0, 0, 0);
|
|
193
|
+
this.currentRotation.status = 'disposed';
|
|
124
194
|
}
|
|
125
195
|
|
|
126
196
|
/**
|
|
127
197
|
* @param {{axis: "x"|"y"|"z", layers: (-1|0|1)[], direction: 1|-1|2|-2}} input
|
|
128
198
|
*/
|
|
129
199
|
rotate(input) {
|
|
200
|
+
var queueLength = this.rotationQueue.length;
|
|
201
|
+
if (queueLength > 0 && this.rotationQueue[queueLength - 1].rotation.axis === input.axis) {
|
|
202
|
+
}
|
|
130
203
|
this.rotationQueue.push(new CubeRotation(input));
|
|
131
204
|
}
|
|
132
205
|
|
|
@@ -150,6 +223,9 @@ export default class Cube {
|
|
|
150
223
|
});
|
|
151
224
|
}
|
|
152
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @returns {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] }}
|
|
228
|
+
*/
|
|
153
229
|
getStickerState() {
|
|
154
230
|
const state = {
|
|
155
231
|
up: [[], [], []],
|
package/src/cube/cubeRotation.js
CHANGED
|
@@ -10,22 +10,18 @@ export class CubeRotation {
|
|
|
10
10
|
/** @type {"pending" | "initialised" | "complete" | "disposed"} */
|
|
11
11
|
this.status = 'pending';
|
|
12
12
|
/** @type {number} */
|
|
13
|
+
this.timestampMs = performance.now();
|
|
14
|
+
/** @type {number} */
|
|
13
15
|
this._lastUpdatedTimeMs = undefined;
|
|
14
|
-
this._startTimeMs = undefined;
|
|
15
16
|
/** @type {number} */
|
|
16
17
|
this._rotationPercentage = 0;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
initialise() {
|
|
20
21
|
this._lastUpdatedTimeMs = performance.now();
|
|
21
|
-
this._startTimeMs = this._lastUpdatedTimeMs;
|
|
22
22
|
this.status = 'initialised';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
dispose() {
|
|
26
|
-
this.status = 'disposed';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
25
|
/**
|
|
30
26
|
*
|
|
31
27
|
* @param {Group} rotationGroup
|
|
@@ -53,8 +49,12 @@ export class CubeRotation {
|
|
|
53
49
|
rotationIncrement,
|
|
54
50
|
);
|
|
55
51
|
|
|
56
|
-
if (this._rotationPercentage
|
|
52
|
+
if (this._rotationPercentage === 100) {
|
|
57
53
|
this.status = 'complete';
|
|
58
54
|
}
|
|
55
|
+
|
|
56
|
+
if (this._rotationPercentage > 100) {
|
|
57
|
+
throw new Error('rotation percentage > 100');
|
|
58
|
+
}
|
|
59
59
|
}
|
|
60
60
|
}
|