@houstonp/rubiks-cube 1.4.0 → 1.5.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
@@ -1,6 +1,6 @@
1
1
  # Rubiks Cube Web Component
2
2
 
3
- This package is a rubiks cube web component built with threejs. Camera animation smoothing is done with the tweenjs package.
3
+ This package is a rubiks cube web component built with threejs. Camera animation smoothing is done with the gsap package.
4
4
 
5
5
  ![cube](cube.png)
6
6
 
@@ -21,7 +21,7 @@ import '@houstonp/rubiks-cube';
21
21
  <meta charset="utf-8" />
22
22
  </head>
23
23
  <body>
24
- <rubiks-cube animation-speed="1000" animation-style="exponential" piece-gap="1.04" camera-speed="100"> </rubiks-cube>
24
+ <rubiks-cube animation-speed-ms="1000" animation-style="exponential" piece-gap="1.04" camera-speed="100"> </rubiks-cube>
25
25
  <script type="module" src="index.js"></script>
26
26
  </body>
27
27
  </html>
@@ -29,12 +29,16 @@ import '@houstonp/rubiks-cube';
29
29
 
30
30
  ## component attributes
31
31
 
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 |
32
+ | attribute | accepted values | Description |
33
+ | ---------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | animation-speed-ms | integer greater than or equal to 0 | sets the duration 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-ms | greater than or equal to 0 | sets the duration of camera animations in milliseconds |
38
+ | camera-radius | greater than or equal to 4 | sets the camera radius |
39
+ | camera-peek-angle-horizontal | decimal between 0 and 1 | sets the horizontal peek angle |
40
+ | camera-peek-angle-vertical | decimal between 0 and 1 | sets the vertical peek angle |
41
+ | camera-field-of-view | integer between 40 and 100 | sets the fielf of view of the camera |
38
42
 
39
43
  ## state of the component
40
44
 
@@ -156,12 +160,12 @@ var event = new CustomEvent('action', {
156
160
 
157
161
  action IDs for camera actions are as follows
158
162
 
159
- - `peek-right` - Camera is moved to the right of the cube so that the right face is visible
160
- - `peek-left` - Camera is moved to the left of the cube so that the left face is visible
161
- - `peek-top` - Camera is moved above the cube so that the top face is visible
162
- - `peek-bottom` - Camera is moved below the cube so that the bottom face is visible
163
- - `peek-toggle-horizontal` - Camera is moved to the opposite side of the cube in the horizontal plane
164
- - `peek-toggle-vertical` - Camera is moved to the opposite side of the cube in the vertical plane
163
+ - `peek-right` - Camera is moved to the right of the cube so that the right face is visible
164
+ - `peek-left` - Camera is moved to the left of the cube so that the left face is visible
165
+ - `peek-top` - Camera is moved above the cube so that the top face is visible
166
+ - `peek-bottom` - Camera is moved below the cube so that the bottom face is visible
167
+ - `peek-toggle-horizontal` - Camera is moved to the opposite side of the cube in the horizontal plane
168
+ - `peek-toggle-vertical` - Camera is moved to the opposite side of the cube in the vertical plane
165
169
 
166
170
  #### Example
167
171
 
@@ -183,15 +187,15 @@ cube.dispatchEvent(event);
183
187
 
184
188
  actionIDs for action type "rotation" are as follows
185
189
 
186
- - 'x',
187
- - 'x2',
188
- - "x'",
189
- - 'y',
190
- - 'y2',
191
- - "y'",
192
- - 'z',
193
- - 'z2',
194
- - "z'",
190
+ - 'x',
191
+ - 'x2',
192
+ - "x'",
193
+ - 'y',
194
+ - 'y2',
195
+ - "y'",
196
+ - 'z',
197
+ - 'z2',
198
+ - "z'",
195
199
 
196
200
  #### Example
197
201
 
@@ -225,25 +229,25 @@ cube.dispatchEvent(event);
225
229
 
226
230
  actionIDs for action type "movement" are as follows
227
231
 
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...
232
+ - 'R',
233
+ - 'R2',
234
+ - "R'",
235
+ - 'L',
236
+ - 'L2',
237
+ - "L'",
238
+ - 'U',
239
+ - 'U2',
240
+ - "U'",
241
+ - 'D',
242
+ - 'D2',
243
+ - "D'",
244
+ - 'F',
245
+ - 'F2',
246
+ - "F'",
247
+ - 'B',
248
+ - 'B2',
249
+ - "B'",
250
+ - etc...
247
251
 
248
252
  #### Example
249
253
 
package/index.js CHANGED
@@ -1,54 +1,105 @@
1
- import { Scene, WebGLRenderer, PerspectiveCamera, AmbientLight, DirectionalLight } from 'three';
2
- import { Tween, Group, Easing } from '@tweenjs/tween.js';
1
+ import { Scene, WebGLRenderer, PerspectiveCamera, AmbientLight, DirectionalLight, Spherical } from 'three';
3
2
  import { OrbitControls } from 'three/examples/jsm/Addons.js';
4
3
  import Cube from './src/cube/cube';
5
4
  import getRotationDetailsFromNotation from './src/utils/rotation';
6
5
  import { debounce } from './src/utils/debouncer';
6
+ import gsap from 'gsap';
7
7
 
8
- const defaultAnimationSpeed = 100;
9
- const defaultCameraSpeed = 100;
10
- const defaultAnimationStyle = 'fixed';
11
- const defaultGap = 1.04;
12
- const minimumGap = 1;
8
+ /** @typedef {{ animationStyle: "exponential" | "next" | "fixed" | "match", animationSpeedMs: number, pieceGap: number, cameraSpeedMs: number, cameraRadius: number, cameraPeekAngleHorizontal: number, cameraPeekAngleVertical: number, cameraFieldOfView: number }} Settings */
9
+ /** @type {Settings} */
10
+ const defaultSettings = {
11
+ animationSpeedMs: 100,
12
+ animationStyle: 'fixed',
13
+ pieceGap: 1.04,
14
+ cameraSpeedMs: 100,
15
+ cameraRadius: 5,
16
+ cameraPeekAngleHorizontal: 0.6,
17
+ cameraPeekAngleVertical: 0.6,
18
+ cameraFieldOfView: 75,
19
+ };
20
+ const minGap = 1;
21
+ const minRadius = 4;
22
+ const minFieldOfView = 40;
23
+ const maxFieldOfView = 100;
24
+ const maxAzimuthAngle = (5 * Math.PI) / 16;
25
+ const polarAngleOffset = Math.PI / 2;
26
+ const maxPolarAngle = (5 * Math.PI) / 16;
13
27
 
14
28
  class RubiksCube extends HTMLElement {
15
29
  constructor() {
16
30
  super();
17
- /** @type {number} */
18
- this.animationSpeed = defaultAnimationSpeed;
19
31
  this.attachShadow({ mode: 'open' });
20
32
  this.shadowRoot.innerHTML = `<canvas id="cube-canvas" style="display:block;"></canvas>`;
21
33
  this.canvas = this.shadowRoot.getElementById('cube-canvas');
22
- /** @type {{animationStyle: "exponential" | "next" | "fixed" | "match", animationSpeed: number, gap: number, cameraSpeed: number}} */
34
+ /** @type {Settings} */
23
35
  this.settings = {
24
- animationSpeed: this.getAttribute('animation-speed') || defaultAnimationSpeed,
25
- animationStyle: this.getAttribute('animation-style') || defaultAnimationStyle,
26
- gap: this.getAttribute('piece-gap') || defaultGap,
27
- cameraSpeed: this.getAttribute('camera-speed') || defaultCameraSpeed,
36
+ animationSpeedMs: this.getAttribute('animation-speed-ms') || defaultSettings.animationSpeedMs,
37
+ animationStyle: this.getAttribute('animation-style') || defaultSettings.animationStyle,
38
+ pieceGap: this.getAttribute('piece-gap') || defaultSettings.pieceGap,
39
+ cameraSpeedMs: this.getAttribute('camera-speed-ms') || defaultSettings.cameraSpeedMs,
40
+ cameraRadius: this.getAttribute('camera-radius') || defaultSettings.cameraRadius,
41
+ cameraPeekAngleHorizontal: this.getAttribute('camera-peek-angle-horizontal') || defaultSettings.cameraPeekAngleHorizontal,
42
+ cameraPeekAngleVertical: this.getAttribute('camera-peek-angle-vertical') || defaultSettings.cameraPeekAngleVertical,
43
+ cameraFieldOfView: this.getAttribute('camera-field-of-view') || defaultSettings.cameraFieldOfView,
28
44
  };
29
45
  }
30
46
 
31
47
  static get observedAttributes() {
32
- return ['animation-style', 'animation-speed', 'piece-gap', 'camera-speed'];
48
+ return [
49
+ 'animation-style',
50
+ 'animation-speed-ms',
51
+ 'piece-gap',
52
+ 'camera-speed-ms',
53
+ 'camera-radius',
54
+ 'camera-peek-angle-horizontal',
55
+ 'camera-peek-angle-vertical',
56
+ 'camera-field-of-view',
57
+ ];
33
58
  }
34
59
 
35
60
  attributeChangedCallback(name, oldVal, newVal) {
36
61
  if (name === 'animation-style') {
37
62
  this.settings.animationStyle = newVal;
38
63
  }
39
- if (name === 'animation-speed') {
64
+ if (name === 'animation-speed-ms') {
40
65
  var speed = Number(newVal);
41
- this.settings.animationSpeed = speed > 0 ? speed : 0;
66
+ this.settings.animationSpeedMs = speed > 0 ? speed : 0;
42
67
  }
43
68
  if (name === 'piece-gap') {
44
69
  var gap = Number(newVal);
45
- this.settings.gap = gap < minimumGap ? minimumGap : gap;
70
+ this.settings.pieceGap = gap < minGap ? minGap : gap;
46
71
  }
47
- if (name === 'camera-speed') {
72
+ if (name === 'camera-speed-ms') {
48
73
  var speed = Number(newVal);
49
- this.settings.cameraSpeed = speed > 0 ? speed : 0;
74
+ this.settings.cameraSpeedMs = speed > 0 ? speed : 0;
75
+ }
76
+ if (name === 'camera-radius') {
77
+ var radius = Number(newVal);
78
+ this.settings.cameraRadius = radius < minRadius ? minRadius : radius;
79
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
80
+ }
81
+ if (name === 'camera-peek-angle-horizontal') {
82
+ var angle = Number(newVal);
83
+ angle = angle > 0 ? angle : 0;
84
+ angle = angle < 1 ? angle : 1;
85
+ this.settings.cameraPeekAngleHorizontal = angle > 0 ? angle : 0;
86
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
87
+ }
88
+ if (name === 'camera-peek-angle-vertical') {
89
+ var angle = Number(newVal);
90
+ angle = angle > 0 ? angle : 0;
91
+ angle = angle < 1 ? angle : 1;
92
+ this.settings.cameraPeekAngleVertical = angle;
93
+ this.dispatchEvent(new CustomEvent('cameraSettingsChanged'));
94
+ }
95
+ if (name == 'camera-field-of-view') {
96
+ var fov = Number(newVal);
97
+ fov = fov > minFieldOfView ? fov : minFieldOfView;
98
+ fov = fov < maxFieldOfView ? fov : maxFieldOfView;
99
+ this.settings.cameraFieldOfView = fov;
50
100
  }
51
101
  }
102
+
52
103
  connectedCallback() {
53
104
  this.init();
54
105
  }
@@ -77,22 +128,21 @@ class RubiksCube extends HTMLElement {
77
128
  ).observe(this);
78
129
 
79
130
  // add camera
80
- const camera = new PerspectiveCamera(75, this.clientWidth / this.clientHeight, 0.1, 1000);
81
- /** @type {{Up: boolean, Right: boolean, UpDistance: number, RightDistance: number}} */
82
- const cameraState = { Up: true, Right: true, UpDistance: 2.5, RightDistance: 2.5 };
83
- camera.position.z = 4;
84
- camera.position.y = 3;
85
- camera.position.x = 0;
131
+ const camera = new PerspectiveCamera(this.settings.cameraFieldOfView, this.clientWidth / this.clientHeight, 0.1, 2000);
132
+ const cameraSpherical = new Spherical(15, (3 * Math.PI) / 8, -Math.PI / 4);
133
+ camera.position.setFromSpherical(cameraSpherical);
134
+ /** @type {{ Up: boolean, Right: boolean }} */
135
+ const cameraState = { Up: true, Right: true };
86
136
 
87
137
  // add orbit controls for camera
88
138
  const controls = new OrbitControls(camera, renderer.domElement);
89
139
  controls.enableZoom = false;
90
140
  controls.enablePan = false;
91
141
  controls.enableDamping = true;
92
- controls.maxAzimuthAngle = Math.PI / 4;
93
- controls.minAzimuthAngle = -Math.PI / 4;
94
- controls.maxPolarAngle = (3 * Math.PI) / 4;
95
- controls.minPolarAngle = Math.PI / 4;
142
+ controls.maxAzimuthAngle = maxAzimuthAngle;
143
+ controls.minAzimuthAngle = -maxAzimuthAngle;
144
+ controls.maxPolarAngle = polarAngleOffset + maxPolarAngle;
145
+ controls.minPolarAngle = polarAngleOffset - maxPolarAngle;
96
146
 
97
147
  // add lighting to scene
98
148
  const ambientLight = new AmbientLight('white', 0.5);
@@ -110,10 +160,6 @@ class RubiksCube extends HTMLElement {
110
160
  const cube = new Cube(this.settings);
111
161
  scene.add(cube.group, cube.rotationGroup);
112
162
 
113
- // initial camera animation
114
- const cameraAnimationGroup = new Group();
115
- cameraAnimationGroup.add(new Tween(camera.position).to({ x: 2.5, y: 2.5, z: 4 }, 1000).easing(Easing.Cubic.InOut).start());
116
-
117
163
  const sendState = (eventId) => {
118
164
  const event = new CustomEvent('state', { detail: { eventId, state: cube.currentState } });
119
165
  this.dispatchEvent(event);
@@ -121,7 +167,6 @@ class RubiksCube extends HTMLElement {
121
167
 
122
168
  // animation loop
123
169
  function animate() {
124
- cameraAnimationGroup.update();
125
170
  controls.update();
126
171
 
127
172
  var eventId = cube.update();
@@ -140,9 +185,9 @@ class RubiksCube extends HTMLElement {
140
185
  this.addEventListener('action', (e) => {
141
186
  /** @type {{eventId: string, action: {type: "movement" | "camera" | "rotation", actionId: string }}} move */
142
187
  var move = e.detail.move;
143
- console.log(move);
144
188
  if (move.action.type === 'camera') {
145
- handleCameraAction(move.action.actionId);
189
+ updateCameraState(move.action.actionId);
190
+ updateCameraPosition();
146
191
  return;
147
192
  }
148
193
  if (move.action.type === 'movement' || move.action.type === 'rotation') {
@@ -163,9 +208,9 @@ class RubiksCube extends HTMLElement {
163
208
  };
164
209
 
165
210
  /**
166
- * @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down'} actionId
211
+ * @param {'peek-toggle-horizontal' | 'peek-toggle-vertical' | 'peek-right' | 'peek-left' | 'peek-up' | 'peek-down' } actionId
167
212
  */
168
- const handleCameraAction = (actionId) => {
213
+ const updateCameraState = (actionId) => {
169
214
  if (actionId === 'peek-toggle-horizontal') {
170
215
  cameraState.Right = !cameraState.Right;
171
216
  } else if (actionId === 'peek-toggle-vertical') {
@@ -179,19 +224,37 @@ class RubiksCube extends HTMLElement {
179
224
  } else if (actionId === 'peek-down') {
180
225
  cameraState.Up = false;
181
226
  }
182
- cameraAnimationGroup.add(
183
- new Tween(camera.position)
184
- .to(
185
- {
186
- x: cameraState.Right ? cameraState.RightDistance : -cameraState.RightDistance,
187
- y: cameraState.Up ? cameraState.UpDistance : -cameraState.UpDistance,
188
- z: 4,
189
- },
190
- this.settings.cameraSpeed,
191
- )
192
- .start(),
193
- );
194
227
  };
228
+
229
+ /**
230
+ * @param {number | null} cameraSpeedMs
231
+ * @param {gsap.EaseString | gsap.EaseFunction | undefined} ease
232
+ */
233
+ const updateCameraPosition = (cameraSpeedMs = this.settings.cameraSpeedMs, ease = 'none') => {
234
+ cameraSpeedMs = cameraSpeedMs ? cameraSpeedMs : this.settings.cameraSpeedMs;
235
+ var phi = polarAngleOffset + (cameraState.Up ? -this.settings.cameraPeekAngleVertical : this.settings.cameraPeekAngleVertical) * maxPolarAngle;
236
+ var theta = (cameraState.Right ? this.settings.cameraPeekAngleHorizontal : -this.settings.cameraPeekAngleHorizontal) * maxAzimuthAngle;
237
+ const startSpherical = new Spherical().setFromVector3(camera.position);
238
+ const targetSpherical = new Spherical(this.settings.cameraRadius, phi, theta);
239
+ gsap.to(startSpherical, {
240
+ radius: targetSpherical.radius,
241
+ theta: targetSpherical.theta,
242
+ phi: targetSpherical.phi,
243
+ duration: cameraSpeedMs / 1000,
244
+ ease: ease,
245
+ onUpdate: function () {
246
+ camera.position.setFromSpherical(startSpherical);
247
+ camera.lookAt(cube.group.position);
248
+ controls.update();
249
+ },
250
+ });
251
+ };
252
+
253
+ this.addEventListener('cameraSettingsChanged', () => {
254
+ updateCameraPosition(); // animate settings changes
255
+ });
256
+
257
+ updateCameraPosition(1000, 'sine.out'); // initial animation
195
258
  }
196
259
  }
197
260
  customElements.define('rubiks-cube', RubiksCube);
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@houstonp/rubiks-cube",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Rubiks Cube Web Component built with threejs",
5
5
  "main": "index.js",
6
6
  "author": "Houston Pearse",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
- "@tweenjs/tween.js": "^25.0.0",
9
+ "gsap": "^3.14.2",
10
10
  "three": "^0.167.1"
11
11
  },
12
- "devDependencies": {},
13
12
  "repository": {
14
13
  "type": "git",
15
14
  "url": "git+https://github.com/houstonpearse/rubiks-cube.git"
package/src/cube/cube.js CHANGED
@@ -5,10 +5,10 @@ import { CubeRotation } from './cubeRotation';
5
5
 
6
6
  export default class Cube {
7
7
  /**
8
- * @param {{style: "exponential" | "next" | "fixed", speed: number, gap: number}} settings
8
+ * @param {import('../..').Settings} settings
9
9
  */
10
10
  constructor(settings) {
11
- /** @type {{animationStyle: "match" | "exponential" | "next" | "fixed", animationSpeed: number, gap: number}} */
11
+ /** @type {import('../..').Settings} */
12
12
  this.settings = settings;
13
13
  /** @type {Group} */
14
14
  this.group = this.createCubeGroup();
@@ -45,7 +45,11 @@ export default class Cube {
45
45
 
46
46
  for (const piece of createCubeState()) {
47
47
  var pieceGroup = piece.group;
48
- pieceGroup.position.set(piece.position.x * this.settings.gap, piece.position.y * this.settings.gap, piece.position.z * this.settings.gap);
48
+ pieceGroup.position.set(
49
+ piece.position.x * this.settings.pieceGap,
50
+ piece.position.y * this.settings.pieceGap,
51
+ piece.position.z * this.settings.pieceGap,
52
+ );
49
53
  pieceGroup.rotation.set(piece.rotation.x, piece.rotation.y, piece.rotation.z);
50
54
  pieceGroup.userData = {
51
55
  position: Object.assign({}, piece.position),
@@ -61,11 +65,11 @@ export default class Cube {
61
65
 
62
66
  /**
63
67
  * update the cube and continue any rotations
64
- * @returns {{ up: string[][], down: string[][], front: string[][], back: string[][], left: string[][], right: string[][] } | undefined }
68
+ * @returns {string}
65
69
  */
66
70
  update() {
67
71
  if (this.currentRotation === undefined) {
68
- if (this._lastGap !== this.settings.gap) {
72
+ if (this._lastGap !== this.settings.pieceGap) {
69
73
  this.updateGap();
70
74
  }
71
75
  this.currentRotation = this.rotationQueue.shift();
@@ -100,9 +104,9 @@ export default class Cube {
100
104
  if (this.currentRotation === undefined) {
101
105
  this.group.children.forEach((piece) => {
102
106
  var { x, y, z } = piece.userData.position;
103
- piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
107
+ piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
104
108
  });
105
- this._lastGap = this.settings.gap;
109
+ this._lastGap = this.settings.pieceGap;
106
110
  }
107
111
  }
108
112
 
@@ -118,15 +122,15 @@ export default class Cube {
118
122
  */
119
123
  getRotationSpeed() {
120
124
  if (this.settings.animationStyle === 'exponential') {
121
- return this.settings.animationSpeed / 2 ** this.rotationQueue.length;
125
+ return this.settings.animationSpeedMs / 2 ** this.rotationQueue.length;
122
126
  }
123
127
  if (this.settings.animationStyle === 'next') {
124
- return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeed;
128
+ return this.rotationQueue.length > 0 ? 0 : this.settings.animationSpeedMs;
125
129
  }
126
130
  if (this.settings.animationStyle === 'match') {
127
131
  if (this.rotationQueue.length > 0) {
128
132
  var lastTimeStamp = this.currentRotation.timestampMs;
129
- var minGap = this._matchSpeed ?? this.settings.animationSpeed;
133
+ var minGap = this._matchSpeed ?? this.settings.animationSpeedMs;
130
134
  for (var i = 0; i < this.rotationQueue.length; i++) {
131
135
  var gap = this.rotationQueue[i].timestampMs - lastTimeStamp;
132
136
  if (gap < minGap) {
@@ -138,12 +142,12 @@ export default class Cube {
138
142
  if (this._matchSpeed !== undefined) {
139
143
  return this._matchSpeed;
140
144
  }
141
- return this.settings.animationSpeed;
145
+ return this.settings.animationSpeedMs;
142
146
  }
143
147
  if (this.settings.animationStyle === 'fixed') {
144
- return this.settings.animationSpeed;
148
+ return this.settings.animationSpeedMs;
145
149
  }
146
- return this.settings.animationSpeed;
150
+ return this.settings.animationSpeedMs;
147
151
  }
148
152
 
149
153
  /**
@@ -160,7 +164,7 @@ export default class Cube {
160
164
  this.group.children.forEach((piece) => {
161
165
  const { x, y, z } = piece.userData.initialPosition;
162
166
  const { x: u, y: v, z: w } = piece.userData.initialRotation;
163
- piece.position.set(x * this.settings.gap, y * this.settings.gap, z * this.settings.gap);
167
+ piece.position.set(x * this.settings.pieceGap, y * this.settings.pieceGap, z * this.settings.pieceGap);
164
168
  piece.rotation.set(u, v, w);
165
169
  piece.userData.position.x = x;
166
170
  piece.userData.position.y = y;