rubiks-cube-3d 1.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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/core-CRe_v4Mo.cjs +2 -0
- package/dist/core-CRe_v4Mo.cjs.map +1 -0
- package/dist/core-L1AjC-fV.js +932 -0
- package/dist/core-L1AjC-fV.js.map +1 -0
- package/dist/core.js +2 -0
- package/dist/core.umd.cjs +1 -0
- package/dist/rubiks-cube-3d.js +90 -0
- package/dist/rubiks-cube-3d.js.map +1 -0
- package/dist/rubiks-cube-3d.standalone.js +4109 -0
- package/dist/rubiks-cube-3d.standalone.js.map +1 -0
- package/dist/rubiks-cube-3d.umd.cjs +2 -0
- package/dist/rubiks-cube-3d.umd.cjs.map +1 -0
- package/dist/types/RubiksCube.d.ts +36 -0
- package/dist/types/core/RubiksCubeEngine.d.ts +113 -0
- package/dist/types/core/geometry.d.ts +38 -0
- package/dist/types/core/index.d.ts +8 -0
- package/dist/types/core/mount.d.ts +26 -0
- package/dist/types/core/notation.d.ts +7 -0
- package/dist/types/core/physics.d.ts +60 -0
- package/dist/types/core/types.d.ts +150 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/standalone.d.ts +20 -0
- package/package.json +80 -0
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
import * as e from "three";
|
|
2
|
+
import { RoomEnvironment as t } from "three/examples/jsm/environments/RoomEnvironment.js";
|
|
3
|
+
import n from "matter-js";
|
|
4
|
+
import { RoundedBoxGeometry as r } from "three/examples/jsm/geometries/RoundedBoxGeometry.js";
|
|
5
|
+
//#region src/core/physics.ts
|
|
6
|
+
var i = 1e3 / 60, a = 6, o = 6.5, s = class {
|
|
7
|
+
constructor(e) {
|
|
8
|
+
this.accumulator = 0, this.elapsed = 0, this.spinningUp = !1, this.bobbing = !1, this.snapping = !1, this.opts = e, this.engine = n.Engine.create({ gravity: {
|
|
9
|
+
x: 0,
|
|
10
|
+
y: 0,
|
|
11
|
+
scale: 0
|
|
12
|
+
} }), this.flywheel = n.Bodies.circle(0, 0, 1, {
|
|
13
|
+
frictionAir: e.scrambleFriction,
|
|
14
|
+
collisionFilter: {
|
|
15
|
+
group: -1,
|
|
16
|
+
mask: 0
|
|
17
|
+
}
|
|
18
|
+
}), n.Body.setMass(this.flywheel, 1), this.floater = n.Bodies.circle(0, 0, 1, {
|
|
19
|
+
frictionAir: .012,
|
|
20
|
+
collisionFilter: {
|
|
21
|
+
group: -1,
|
|
22
|
+
mask: 0
|
|
23
|
+
}
|
|
24
|
+
}), n.Body.setMass(this.floater, 1), this.floatSpring = n.Constraint.create({
|
|
25
|
+
pointA: {
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0
|
|
28
|
+
},
|
|
29
|
+
bodyB: this.floater,
|
|
30
|
+
length: 0,
|
|
31
|
+
stiffness: e.floatStiffness,
|
|
32
|
+
damping: e.floatDamping
|
|
33
|
+
}), this.snapper = n.Bodies.circle(0, 0, 1, {
|
|
34
|
+
frictionAir: 0,
|
|
35
|
+
collisionFilter: {
|
|
36
|
+
group: -1,
|
|
37
|
+
mask: 0
|
|
38
|
+
}
|
|
39
|
+
}), n.Body.setMass(this.snapper, 1), this.snapSpring = n.Constraint.create({
|
|
40
|
+
pointA: {
|
|
41
|
+
x: 0,
|
|
42
|
+
y: 0
|
|
43
|
+
},
|
|
44
|
+
bodyB: this.snapper,
|
|
45
|
+
length: 0,
|
|
46
|
+
stiffness: e.snapStiffness,
|
|
47
|
+
damping: e.snapDamping
|
|
48
|
+
}), n.Composite.add(this.engine.world, [
|
|
49
|
+
this.flywheel,
|
|
50
|
+
this.floater,
|
|
51
|
+
this.floatSpring,
|
|
52
|
+
this.snapper,
|
|
53
|
+
this.snapSpring
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
setOptions(e) {
|
|
57
|
+
this.opts = e, this.flywheel.frictionAir = e.scrambleFriction, this.floatSpring.stiffness = e.floatStiffness, this.floatSpring.damping = e.floatDamping, this.snapSpring.stiffness = e.snapStiffness, this.snapSpring.damping = e.snapDamping;
|
|
58
|
+
}
|
|
59
|
+
resetFlywheel() {
|
|
60
|
+
n.Body.setPosition(this.flywheel, {
|
|
61
|
+
x: 0,
|
|
62
|
+
y: 0
|
|
63
|
+
}), n.Body.setVelocity(this.flywheel, {
|
|
64
|
+
x: 0,
|
|
65
|
+
y: 0
|
|
66
|
+
}), this.spinningUp = !1;
|
|
67
|
+
}
|
|
68
|
+
setSpinningUp(e) {
|
|
69
|
+
this.spinningUp = e;
|
|
70
|
+
}
|
|
71
|
+
get scrambleSpeed() {
|
|
72
|
+
let e = Math.abs(this.flywheel.velocity.x);
|
|
73
|
+
return Math.min(this.opts.maxSpeed, Math.max(this.opts.minSpeed, e));
|
|
74
|
+
}
|
|
75
|
+
get flywheelVelocity() {
|
|
76
|
+
return this.flywheel.velocity.x;
|
|
77
|
+
}
|
|
78
|
+
get quarterTurnsToCoast() {
|
|
79
|
+
let e = Math.abs(this.flywheel.velocity.x), t = Math.max(1e-4, this.opts.scrambleFriction);
|
|
80
|
+
return Math.max(0, e - this.opts.minSpeed) / (60 * t) / (Math.PI / 2);
|
|
81
|
+
}
|
|
82
|
+
setFloatTarget(e) {
|
|
83
|
+
this.floatSpring.pointA.y = e;
|
|
84
|
+
}
|
|
85
|
+
setBobbing(e) {
|
|
86
|
+
this.bobbing = e;
|
|
87
|
+
}
|
|
88
|
+
get floatY() {
|
|
89
|
+
return this.floater.position.y;
|
|
90
|
+
}
|
|
91
|
+
beginSnap(e, t, r) {
|
|
92
|
+
n.Body.setPosition(this.snapper, {
|
|
93
|
+
x: e,
|
|
94
|
+
y: 0
|
|
95
|
+
}), n.Body.setVelocity(this.snapper, {
|
|
96
|
+
x: t / 60,
|
|
97
|
+
y: 0
|
|
98
|
+
}), this.snapSpring.pointA.x = r, this.snapping = !0;
|
|
99
|
+
}
|
|
100
|
+
endSnap() {
|
|
101
|
+
this.snapping = !1;
|
|
102
|
+
}
|
|
103
|
+
get snapAngle() {
|
|
104
|
+
return this.snapper.position.x;
|
|
105
|
+
}
|
|
106
|
+
get snapTarget() {
|
|
107
|
+
return this.snapSpring.pointA.x;
|
|
108
|
+
}
|
|
109
|
+
get snapSettled() {
|
|
110
|
+
return this.snapping && Math.abs(this.snapper.position.x - this.snapSpring.pointA.x) < .004 && Math.abs(this.snapper.velocity.x) < .004;
|
|
111
|
+
}
|
|
112
|
+
step(e) {
|
|
113
|
+
this.accumulator += Math.min(e, .1) * 1e3;
|
|
114
|
+
let t = 0;
|
|
115
|
+
for (; this.accumulator >= i && t < a;) this.substep(), this.accumulator -= i, t++;
|
|
116
|
+
t === a && (this.accumulator = 0);
|
|
117
|
+
}
|
|
118
|
+
substep() {
|
|
119
|
+
this.elapsed += i / 1e3;
|
|
120
|
+
let { opts: e } = this;
|
|
121
|
+
if (this.spinningUp && n.Body.applyForce(this.flywheel, this.flywheel.position, {
|
|
122
|
+
x: e.scrambleForce,
|
|
123
|
+
y: 0
|
|
124
|
+
}), n.Body.setPosition(this.flywheel, {
|
|
125
|
+
x: 0,
|
|
126
|
+
y: 0
|
|
127
|
+
}), this.bobbing) {
|
|
128
|
+
let t = 2 * Math.PI * e.floatFrequency, r = Math.sin(this.elapsed * t) * e.floatAmplitude * e.floatStiffness * o;
|
|
129
|
+
n.Body.applyForce(this.floater, this.floater.position, {
|
|
130
|
+
x: 0,
|
|
131
|
+
y: r / 277.8
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
n.Body.setPosition(this.floater, {
|
|
135
|
+
x: 0,
|
|
136
|
+
y: this.floater.position.y
|
|
137
|
+
}), n.Engine.update(this.engine, i);
|
|
138
|
+
}
|
|
139
|
+
dispose() {
|
|
140
|
+
n.World.clear(this.engine.world, !1), n.Engine.clear(this.engine);
|
|
141
|
+
}
|
|
142
|
+
}, c = {
|
|
143
|
+
R: new e.Vector3(1, 0, 0),
|
|
144
|
+
L: new e.Vector3(-1, 0, 0),
|
|
145
|
+
U: new e.Vector3(0, 1, 0),
|
|
146
|
+
D: new e.Vector3(0, -1, 0),
|
|
147
|
+
F: new e.Vector3(0, 0, 1),
|
|
148
|
+
B: new e.Vector3(0, 0, -1)
|
|
149
|
+
};
|
|
150
|
+
function l(t, n) {
|
|
151
|
+
let r = t / 2, i = new e.Shape();
|
|
152
|
+
return i.moveTo(-r + n, -r), i.lineTo(r - n, -r), i.quadraticCurveTo(r, -r, r, -r + n), i.lineTo(r, r - n), i.quadraticCurveTo(r, r, r - n, r), i.lineTo(-r + n, r), i.quadraticCurveTo(-r, r, -r, r - n), i.lineTo(-r, -r + n), i.quadraticCurveTo(-r, -r, -r + n, -r), i;
|
|
153
|
+
}
|
|
154
|
+
function u(t) {
|
|
155
|
+
return new e.MeshPhysicalMaterial({
|
|
156
|
+
color: new e.Color(t.body),
|
|
157
|
+
roughness: .42,
|
|
158
|
+
metalness: 0,
|
|
159
|
+
clearcoat: .55,
|
|
160
|
+
clearcoatRoughness: .3,
|
|
161
|
+
envMapIntensity: .9
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function d(t, n) {
|
|
165
|
+
return new e.MeshPhysicalMaterial({
|
|
166
|
+
color: new e.Color(t),
|
|
167
|
+
roughness: n,
|
|
168
|
+
metalness: 0,
|
|
169
|
+
clearcoat: .9,
|
|
170
|
+
clearcoatRoughness: .18,
|
|
171
|
+
envMapIntensity: .9
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function f(t) {
|
|
175
|
+
let { size: n, colors: i, sticker: a } = t, o = 1 - t.cubieGap, s = (n - 1) / 2, f = [], p = [], m = new r(o, o, o, 5, t.cubieRounding * o);
|
|
176
|
+
f.push(m);
|
|
177
|
+
let h = u(i);
|
|
178
|
+
p.push(h);
|
|
179
|
+
let g = .006, _ = a.inset * o - 2 * g, v = new e.ExtrudeGeometry(l(_, a.cornerRadius * o), {
|
|
180
|
+
depth: Math.max(.002, a.height - g),
|
|
181
|
+
bevelEnabled: !0,
|
|
182
|
+
bevelThickness: g,
|
|
183
|
+
bevelSize: g,
|
|
184
|
+
bevelSegments: 2,
|
|
185
|
+
curveSegments: 6
|
|
186
|
+
});
|
|
187
|
+
v.computeVertexNormals(), f.push(v);
|
|
188
|
+
let y = {
|
|
189
|
+
U: d(i.U, a.roughness),
|
|
190
|
+
D: d(i.D, a.roughness),
|
|
191
|
+
F: d(i.F, a.roughness),
|
|
192
|
+
B: d(i.B, a.roughness),
|
|
193
|
+
L: d(i.L, a.roughness),
|
|
194
|
+
R: d(i.R, a.roughness)
|
|
195
|
+
};
|
|
196
|
+
p.push(...Object.values(y));
|
|
197
|
+
let b = [], x = new e.Vector3(0, 0, 1);
|
|
198
|
+
for (let t = 0; t < n; t++) for (let r = 0; r < n; r++) for (let i = 0; i < n; i++) {
|
|
199
|
+
let n = t - s, a = r - s, l = i - s;
|
|
200
|
+
if (Math.abs(n) !== s && Math.abs(a) !== s && Math.abs(l) !== s) continue;
|
|
201
|
+
let u = new e.Group();
|
|
202
|
+
u.position.set(n, a, l);
|
|
203
|
+
let d = new e.Mesh(m, h);
|
|
204
|
+
d.castShadow = !0, d.receiveShadow = !0, u.add(d);
|
|
205
|
+
let f = [], p = [];
|
|
206
|
+
n === s && p.push("R"), n === -s && p.push("L"), a === s && p.push("U"), a === -s && p.push("D"), l === s && p.push("F"), l === -s && p.push("B");
|
|
207
|
+
for (let t of p) {
|
|
208
|
+
let n = c[t], r = new e.Mesh(v, y[t]);
|
|
209
|
+
r.quaternion.setFromUnitVectors(x, n), r.position.copy(n).multiplyScalar(o / 2 - .002), r.castShadow = !1, r.receiveShadow = !0, r.userData.face = t, u.add(r), f.push({
|
|
210
|
+
face: t,
|
|
211
|
+
localNormal: n.clone(),
|
|
212
|
+
mesh: r
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
let g = {
|
|
216
|
+
object: u,
|
|
217
|
+
body: d,
|
|
218
|
+
stickers: f,
|
|
219
|
+
grid: new e.Vector3(n, a, l),
|
|
220
|
+
home: new e.Vector3(n, a, l)
|
|
221
|
+
};
|
|
222
|
+
u.userData.cubie = g, d.userData.cubie = g, b.push(g);
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
cubies: b,
|
|
226
|
+
resources: {
|
|
227
|
+
geometries: f,
|
|
228
|
+
materials: p
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function p(t) {
|
|
233
|
+
let n = new e.Matrix4().makeRotationFromQuaternion(t), r = n.elements;
|
|
234
|
+
for (let e = 0; e < 16; e++) r[e] = Math.round(r[e]);
|
|
235
|
+
t.setFromRotationMatrix(n);
|
|
236
|
+
}
|
|
237
|
+
function m(e) {
|
|
238
|
+
e.x = Math.round(e.x * 2) / 2, e.y = Math.round(e.y * 2) / 2, e.z = Math.round(e.z * 2) / 2, Object.is(e.x, -0) && (e.x = 0), Object.is(e.y, -0) && (e.y = 0), Object.is(e.z, -0) && (e.z = 0);
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/core/notation.ts
|
|
242
|
+
var h = {
|
|
243
|
+
R: {
|
|
244
|
+
axis: "x",
|
|
245
|
+
sign: 1,
|
|
246
|
+
cw: -1
|
|
247
|
+
},
|
|
248
|
+
L: {
|
|
249
|
+
axis: "x",
|
|
250
|
+
sign: -1,
|
|
251
|
+
cw: 1
|
|
252
|
+
},
|
|
253
|
+
U: {
|
|
254
|
+
axis: "y",
|
|
255
|
+
sign: 1,
|
|
256
|
+
cw: -1
|
|
257
|
+
},
|
|
258
|
+
D: {
|
|
259
|
+
axis: "y",
|
|
260
|
+
sign: -1,
|
|
261
|
+
cw: 1
|
|
262
|
+
},
|
|
263
|
+
F: {
|
|
264
|
+
axis: "z",
|
|
265
|
+
sign: 1,
|
|
266
|
+
cw: -1
|
|
267
|
+
},
|
|
268
|
+
B: {
|
|
269
|
+
axis: "z",
|
|
270
|
+
sign: -1,
|
|
271
|
+
cw: 1
|
|
272
|
+
}
|
|
273
|
+
}, g = {
|
|
274
|
+
M: {
|
|
275
|
+
axis: "x",
|
|
276
|
+
cw: 1
|
|
277
|
+
},
|
|
278
|
+
E: {
|
|
279
|
+
axis: "y",
|
|
280
|
+
cw: 1
|
|
281
|
+
},
|
|
282
|
+
S: {
|
|
283
|
+
axis: "z",
|
|
284
|
+
cw: -1
|
|
285
|
+
}
|
|
286
|
+
}, _ = {
|
|
287
|
+
x: {
|
|
288
|
+
axis: "x",
|
|
289
|
+
cw: -1
|
|
290
|
+
},
|
|
291
|
+
y: {
|
|
292
|
+
axis: "y",
|
|
293
|
+
cw: -1
|
|
294
|
+
},
|
|
295
|
+
z: {
|
|
296
|
+
axis: "z",
|
|
297
|
+
cw: -1
|
|
298
|
+
}
|
|
299
|
+
}, v = /^(\d*)([UDFBLRudfblrMESxyz])(w?)(['’2]*)$/;
|
|
300
|
+
function y(e) {
|
|
301
|
+
let t = (e - 1) / 2, n = [];
|
|
302
|
+
for (let r = 0; r < e; r++) n.push(r - t);
|
|
303
|
+
return n;
|
|
304
|
+
}
|
|
305
|
+
function b(e) {
|
|
306
|
+
let t = 1;
|
|
307
|
+
for (let n of e) t *= n === "2" ? 2 : -1;
|
|
308
|
+
return t;
|
|
309
|
+
}
|
|
310
|
+
function x(e, t) {
|
|
311
|
+
let n = (t - 1) / 2, r = [];
|
|
312
|
+
for (let i of e.trim().split(/\s+/)) {
|
|
313
|
+
if (!i) continue;
|
|
314
|
+
let e = v.exec(i);
|
|
315
|
+
if (!e) throw Error(`rubiks-cube-3d: cannot parse move "${i}"`);
|
|
316
|
+
let [, a, o, s, c] = e, l = b(c), u = a ? parseInt(a, 10) : 0;
|
|
317
|
+
if (o === "x" || o === "y" || o === "z") {
|
|
318
|
+
let e = _[o];
|
|
319
|
+
r.push({
|
|
320
|
+
axis: e.axis,
|
|
321
|
+
layers: y(t),
|
|
322
|
+
quarterTurns: e.cw * l
|
|
323
|
+
});
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (o === "M" || o === "E" || o === "S") {
|
|
327
|
+
let e = g[o], n = t % 2 == 1 ? [0] : [-.5, .5];
|
|
328
|
+
r.push({
|
|
329
|
+
axis: e.axis,
|
|
330
|
+
layers: n,
|
|
331
|
+
quarterTurns: e.cw * l
|
|
332
|
+
});
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
let d = o === o.toLowerCase(), f = h[o.toUpperCase()], p = d || s === "w", m = [];
|
|
336
|
+
if (p) {
|
|
337
|
+
let e = Math.min(t - 1, Math.max(2, u || 2));
|
|
338
|
+
for (let t = 0; t < e; t++) m.push(f.sign * (n - t));
|
|
339
|
+
} else u > 1 ? m.push(f.sign * (n - Math.min(u, t) + 1)) : m.push(f.sign * n);
|
|
340
|
+
r.push({
|
|
341
|
+
axis: f.axis,
|
|
342
|
+
layers: m,
|
|
343
|
+
quarterTurns: f.cw * l
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return r;
|
|
347
|
+
}
|
|
348
|
+
function S(e, t) {
|
|
349
|
+
let n = (t - 1) / 2, r = (e.quarterTurns % 4 + 4) % 4;
|
|
350
|
+
if (r === 0) return "";
|
|
351
|
+
let i = (e) => {
|
|
352
|
+
let t = e * (r === 3 ? -1 : r);
|
|
353
|
+
return t === 2 || t === -2 ? "2" : t > 0 ? "" : "'";
|
|
354
|
+
}, a = [...e.layers].sort((e, t) => e - t);
|
|
355
|
+
if (a.length === t) {
|
|
356
|
+
let t = _[e.axis];
|
|
357
|
+
return `${e.axis}${i(t.cw)}`;
|
|
358
|
+
}
|
|
359
|
+
let o = a.find((e) => Math.abs(e) === n), s = o === void 0 ? a[a.length - 1] > 0 : o > 0, c = Object.keys(h).find((t) => h[t].axis === e.axis && h[t].sign > 0 === s), l = h[c];
|
|
360
|
+
if (a.length === 1) {
|
|
361
|
+
let r = a[0];
|
|
362
|
+
if (Math.abs(r) === n) return `${c}${i(l.cw)}`;
|
|
363
|
+
if (t % 2 == 1 && r === 0) {
|
|
364
|
+
let t = e.axis === "x" ? "M" : e.axis === "y" ? "E" : "S";
|
|
365
|
+
return `${t}${i(g[t].cw)}`;
|
|
366
|
+
}
|
|
367
|
+
return `${Math.round(n - Math.abs(r)) + 1}${c}${i(l.cw)}`;
|
|
368
|
+
}
|
|
369
|
+
if (t % 2 == 0 && a.length === 2 && a[0] === -.5 && a[1] === .5) {
|
|
370
|
+
let t = e.axis === "x" ? "M" : e.axis === "y" ? "E" : "S";
|
|
371
|
+
return `${t}${i(g[t].cw)}`;
|
|
372
|
+
}
|
|
373
|
+
let u = a.length;
|
|
374
|
+
return `${u > 2 ? u : ""}${c}w${i(l.cw)}`;
|
|
375
|
+
}
|
|
376
|
+
function C(e, t, n = Math.random) {
|
|
377
|
+
let r = [
|
|
378
|
+
"x",
|
|
379
|
+
"y",
|
|
380
|
+
"z"
|
|
381
|
+
], i = y(e), a = (e - 1) / 2, o = [], s = null, c = 0;
|
|
382
|
+
for (let l = 0; l < t; l++) {
|
|
383
|
+
let t;
|
|
384
|
+
do
|
|
385
|
+
t = r[Math.floor(n() * 3)];
|
|
386
|
+
while (t === s && c >= 1 && n() < .85);
|
|
387
|
+
c = t === s ? c + 1 : 0, s = t;
|
|
388
|
+
let l;
|
|
389
|
+
l = e === 3 || n() < .7 ? n() < .5 ? a : -a : i[Math.floor(n() * i.length)];
|
|
390
|
+
let u = n() < .15 ? 2 : n() < .5 ? 1 : -1;
|
|
391
|
+
o.push({
|
|
392
|
+
axis: t,
|
|
393
|
+
layers: [l],
|
|
394
|
+
quarterTurns: u
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return o;
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/core/types.ts
|
|
401
|
+
var w = {
|
|
402
|
+
U: "#ffd60a",
|
|
403
|
+
D: "#f7f7f7",
|
|
404
|
+
F: "#e5352b",
|
|
405
|
+
B: "#ff7f11",
|
|
406
|
+
L: "#3a7bd5",
|
|
407
|
+
R: "#3cb44b",
|
|
408
|
+
body: "#0b0b0d"
|
|
409
|
+
}, T = {
|
|
410
|
+
scrambleForce: 66e-5,
|
|
411
|
+
scrambleFriction: .013,
|
|
412
|
+
maxSpeed: 11,
|
|
413
|
+
minSpeed: 1.6,
|
|
414
|
+
floatLift: .22,
|
|
415
|
+
floatAmplitude: .09,
|
|
416
|
+
floatFrequency: .45,
|
|
417
|
+
floatStiffness: .004,
|
|
418
|
+
floatDamping: .03,
|
|
419
|
+
idleSpin: .25,
|
|
420
|
+
idleTumble: .06,
|
|
421
|
+
snapStiffness: .03,
|
|
422
|
+
snapDamping: .07
|
|
423
|
+
}, E = {
|
|
424
|
+
enabled: !0,
|
|
425
|
+
dragPixelsPerQuarterTurn: 110,
|
|
426
|
+
orbit: !0,
|
|
427
|
+
orbitSensitivity: .008,
|
|
428
|
+
interruptScramble: !1
|
|
429
|
+
}, D = {
|
|
430
|
+
fov: 32,
|
|
431
|
+
pitch: 31,
|
|
432
|
+
yaw: 38
|
|
433
|
+
}, O = {
|
|
434
|
+
inset: .84,
|
|
435
|
+
cornerRadius: .09,
|
|
436
|
+
height: .018,
|
|
437
|
+
roughness: .28
|
|
438
|
+
}, k = {
|
|
439
|
+
size: 3,
|
|
440
|
+
background: "transparent",
|
|
441
|
+
colors: {},
|
|
442
|
+
scrambleOnMount: !0,
|
|
443
|
+
scrambleMoves: 25,
|
|
444
|
+
scrambleDelay: 350,
|
|
445
|
+
moveDuration: 260,
|
|
446
|
+
physics: {},
|
|
447
|
+
interaction: {},
|
|
448
|
+
camera: {},
|
|
449
|
+
sticker: {},
|
|
450
|
+
shadow: !0,
|
|
451
|
+
shadowOpacity: .28,
|
|
452
|
+
shadowMapSize: 2048,
|
|
453
|
+
cubieRounding: .1,
|
|
454
|
+
cubieGap: .035,
|
|
455
|
+
maxPixelRatio: 2,
|
|
456
|
+
antialias: !0,
|
|
457
|
+
exposure: 1,
|
|
458
|
+
idleOnMount: !0
|
|
459
|
+
};
|
|
460
|
+
function A(e) {
|
|
461
|
+
let t = {};
|
|
462
|
+
if (!e) return t;
|
|
463
|
+
for (let n of Object.keys(e)) e[n] !== void 0 && (t[n] = e[n]);
|
|
464
|
+
return t;
|
|
465
|
+
}
|
|
466
|
+
function j(e = {}) {
|
|
467
|
+
let t = A(e), n = Math.max(2, Math.min(7, Math.round(t.size ?? k.size)));
|
|
468
|
+
return {
|
|
469
|
+
...k,
|
|
470
|
+
...t,
|
|
471
|
+
size: n,
|
|
472
|
+
colors: {
|
|
473
|
+
...w,
|
|
474
|
+
...A(t.colors)
|
|
475
|
+
},
|
|
476
|
+
physics: {
|
|
477
|
+
...T,
|
|
478
|
+
...A(t.physics)
|
|
479
|
+
},
|
|
480
|
+
interaction: {
|
|
481
|
+
...E,
|
|
482
|
+
...A(t.interaction)
|
|
483
|
+
},
|
|
484
|
+
camera: {
|
|
485
|
+
...D,
|
|
486
|
+
...A(t.camera)
|
|
487
|
+
},
|
|
488
|
+
sticker: {
|
|
489
|
+
...O,
|
|
490
|
+
...A(t.sticker)
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/core/RubiksCubeEngine.ts
|
|
496
|
+
var M = Math.PI / 2, N = {
|
|
497
|
+
x: new e.Vector3(1, 0, 0),
|
|
498
|
+
y: new e.Vector3(0, 1, 0),
|
|
499
|
+
z: new e.Vector3(0, 0, 1)
|
|
500
|
+
}, P = class {
|
|
501
|
+
constructor(n, r = {}) {
|
|
502
|
+
this.cubeGroup = new e.Group(), this.cubies = [], this.raycaster = new e.Raycaster(), this.queue = [], this.active = null, this.drag = null, this.scrambleRemaining = 0, this.scrambleResolve = null, this.hovering = !1, this.solved = !0, this.timer = new e.Timer(), this.elapsed = 0, this.rafId = 0, this.disposed = !1, this.touchLeaveTimer = 0, this.listeners = /* @__PURE__ */ new Map(), this.mountTimer = 0, this.squareMode = !1, this.resize = () => {
|
|
503
|
+
let e = this.container.clientWidth || 300;
|
|
504
|
+
this.container.clientHeight || (this.squareMode = !0);
|
|
505
|
+
let t = this.squareMode ? e : this.container.clientHeight;
|
|
506
|
+
this.canvas.style.height = this.squareMode ? `${e}px` : "100%", this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, this.options.maxPixelRatio)), this.renderer.setSize(e, t, !1), this.camera.aspect = e / t, this.camera.updateProjectionMatrix();
|
|
507
|
+
}, this.tick = () => {
|
|
508
|
+
if (this.disposed) return;
|
|
509
|
+
this.rafId = requestAnimationFrame(this.tick), this.timer.update();
|
|
510
|
+
let e = Math.min(this.timer.getDelta(), .1);
|
|
511
|
+
this.elapsed += e, this.physics.step(e), this.updateTurn(e), this.updateIdle(e), this.cubeGroup.position.y = this.physics.floatY, this.renderer.render(this.scene, this.camera);
|
|
512
|
+
}, this.preventDefault = (e) => e.preventDefault(), this.onPointerEnter = (e) => {
|
|
513
|
+
e.pointerType !== "touch" && (window.clearTimeout(this.touchLeaveTimer), this.setHover(!0));
|
|
514
|
+
}, this.onPointerLeave = (e) => {
|
|
515
|
+
e.pointerType !== "touch" && (this.drag || this.setHover(!1));
|
|
516
|
+
}, this.onPointerDown = (e) => {
|
|
517
|
+
let t = this.options.interaction;
|
|
518
|
+
if (!t.enabled || e.pointerType === "mouse" && e.button !== 0 || (e.pointerType === "touch" && (window.clearTimeout(this.touchLeaveTimer), this.setHover(!0)), this.drag)) return;
|
|
519
|
+
let n = this.state === "scrambling";
|
|
520
|
+
if (n && !t.interruptScramble) return;
|
|
521
|
+
let r = this.pickCubie(e);
|
|
522
|
+
if (r) {
|
|
523
|
+
if (this.active) {
|
|
524
|
+
n && this.cancelQueue();
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
this.drag = {
|
|
528
|
+
pointerId: e.pointerId,
|
|
529
|
+
startX: e.clientX,
|
|
530
|
+
startY: e.clientY,
|
|
531
|
+
lastX: e.clientX,
|
|
532
|
+
lastY: e.clientY,
|
|
533
|
+
kind: "undecided",
|
|
534
|
+
cubie: r.cubie,
|
|
535
|
+
normal: r.normal,
|
|
536
|
+
hit: r.point,
|
|
537
|
+
samples: []
|
|
538
|
+
};
|
|
539
|
+
} else if (t.orbit) this.drag = {
|
|
540
|
+
pointerId: e.pointerId,
|
|
541
|
+
startX: e.clientX,
|
|
542
|
+
startY: e.clientY,
|
|
543
|
+
lastX: e.clientX,
|
|
544
|
+
lastY: e.clientY,
|
|
545
|
+
kind: "orbit",
|
|
546
|
+
samples: []
|
|
547
|
+
};
|
|
548
|
+
else return;
|
|
549
|
+
this.canvas.setPointerCapture(e.pointerId), this.canvas.style.cursor = "grabbing", e.preventDefault();
|
|
550
|
+
}, this.onPointerMove = (t) => {
|
|
551
|
+
let n = this.drag;
|
|
552
|
+
if (!n || n.pointerId !== t.pointerId) {
|
|
553
|
+
!n && t.pointerType === "mouse" && (this.canvas.style.cursor = this.pickCubie(t) ? "grab" : "default");
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
let r = t.clientX - n.lastX, i = t.clientY - n.lastY;
|
|
557
|
+
if (n.lastX = t.clientX, n.lastY = t.clientY, n.kind === "orbit") {
|
|
558
|
+
let t = this.options.interaction.orbitSensitivity, n = new e.Quaternion().setFromAxisAngle(N.y, r * t), a = new e.Quaternion().setFromAxisAngle(N.x, i * t);
|
|
559
|
+
this.cubeGroup.quaternion.premultiply(n).premultiply(a);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
let a = t.clientX - n.startX, o = t.clientY - n.startY;
|
|
563
|
+
if (n.kind === "undecided" && (Math.hypot(a, o) < 6 || (this.decideLayerDrag(n, new e.Vector2(a, o)), n.kind !== "layer"))) return;
|
|
564
|
+
let s = new e.Vector2(a, o).dot(n.screenDir) / this.options.interaction.dragPixelsPerQuarterTurn * M * n.sign;
|
|
565
|
+
this.setTurnAngle(s), n.samples.push({
|
|
566
|
+
t: performance.now(),
|
|
567
|
+
angle: s
|
|
568
|
+
}), n.samples.length > 6 && n.samples.shift();
|
|
569
|
+
}, this.onPointerUp = (e) => {
|
|
570
|
+
let t = this.drag;
|
|
571
|
+
if (e.pointerType === "touch" && (window.clearTimeout(this.touchLeaveTimer), this.touchLeaveTimer = window.setTimeout(() => this.setHover(!1), 1500)), t && t.pointerId === e.pointerId) {
|
|
572
|
+
if (this.drag = null, this.canvas.hasPointerCapture(e.pointerId) && this.canvas.releasePointerCapture(e.pointerId), this.canvas.style.cursor = "grab", t.kind === "layer" && this.active?.mode === "drag") {
|
|
573
|
+
let e = this.active, n = 0;
|
|
574
|
+
if (t.samples.length >= 2) {
|
|
575
|
+
let e = t.samples[0], r = t.samples[t.samples.length - 1], i = r.t - e.t;
|
|
576
|
+
i > 0 && (n = (r.angle - e.angle) / i * 1e3);
|
|
577
|
+
}
|
|
578
|
+
let r = e.angle + n * .12, i = Math.floor(e.angle / M) * M, a = Math.ceil(e.angle / M) * M, o = Math.max(i, Math.min(a, Math.round(r / M) * M));
|
|
579
|
+
e.mode = "snap", e.target = o, this.physics.beginSnap(e.angle, n, o);
|
|
580
|
+
}
|
|
581
|
+
if (e.pointerType === "mouse") {
|
|
582
|
+
let t = this.canvas.getBoundingClientRect();
|
|
583
|
+
e.clientX >= t.left && e.clientX <= t.right && e.clientY >= t.top && e.clientY <= t.bottom || this.setHover(!1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}, this.container = n, this.options = j(r), this.idleEnabled = this.options.idleOnMount, this.renderer = new e.WebGLRenderer({
|
|
587
|
+
alpha: !0,
|
|
588
|
+
antialias: this.options.antialias,
|
|
589
|
+
powerPreference: "high-performance",
|
|
590
|
+
premultipliedAlpha: !0
|
|
591
|
+
}), this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = e.PCFShadowMap, this.renderer.toneMapping = e.NeutralToneMapping, this.renderer.toneMappingExposure = this.options.exposure, this.renderer.outputColorSpace = e.SRGBColorSpace, this.canvas = this.renderer.domElement, Object.assign(this.canvas.style, {
|
|
592
|
+
display: "block",
|
|
593
|
+
width: "100%",
|
|
594
|
+
height: "100%",
|
|
595
|
+
touchAction: "none",
|
|
596
|
+
userSelect: "none",
|
|
597
|
+
cursor: "grab",
|
|
598
|
+
outline: "none"
|
|
599
|
+
}), this.canvas.setAttribute("aria-label", "Interactive Rubik's cube"), this.canvas.setAttribute("role", "img"), n.appendChild(this.canvas), this.applyBackground(), this.scene = new e.Scene(), this.pmrem = new e.PMREMGenerator(this.renderer), this.envTexture = this.pmrem.fromScene(new t(), .04).texture, this.scene.environment = this.envTexture, this.scene.environmentIntensity = .7, this.keyLight = new e.DirectionalLight(16777215, 2.4), this.keyLight.position.set(4, 9, 6), this.keyLight.castShadow = !0, this.keyLight.shadow.mapSize.set(this.options.shadowMapSize, this.options.shadowMapSize), this.keyLight.shadow.bias = -4e-4, this.keyLight.shadow.normalBias = .02, this.keyLight.shadow.radius = 4, this.scene.add(this.keyLight), this.scene.add(this.keyLight.target);
|
|
600
|
+
let i = new e.DirectionalLight(14674175, .55);
|
|
601
|
+
i.position.set(-6, 2, -4), this.scene.add(i);
|
|
602
|
+
let a = new e.DirectionalLight(16777215, .35);
|
|
603
|
+
a.position.set(0, -3, -6), this.scene.add(a), this.scene.add(this.cubeGroup), this.camera = new e.PerspectiveCamera(this.options.camera.fov, 1, .1, 100), this.placeCamera(), this.physics = new s(this.options.physics), this.buildCube(), this.buildShadow(), this.resize(), typeof ResizeObserver < "u" ? (this.resizeObserver = new ResizeObserver(() => this.resize()), this.resizeObserver.observe(n)) : window.addEventListener("resize", this.resize), this.bindPointerEvents();
|
|
604
|
+
for (let e of [
|
|
605
|
+
"onMove",
|
|
606
|
+
"onScrambleStart",
|
|
607
|
+
"onScrambleEnd",
|
|
608
|
+
"onSolved",
|
|
609
|
+
"onStateChange",
|
|
610
|
+
"onHoverChange"
|
|
611
|
+
]) {
|
|
612
|
+
let t = this.options[e];
|
|
613
|
+
t && this.on(e.slice(2).replace(/^[A-Z]/, (e) => e.toLowerCase()), t);
|
|
614
|
+
}
|
|
615
|
+
this.applyIdleState(), this.rafId = requestAnimationFrame(this.tick), this.options.scrambleOnMount && (this.mountTimer = window.setTimeout(() => {
|
|
616
|
+
this.mountTimer = 0, this.scramble();
|
|
617
|
+
}, this.options.scrambleDelay));
|
|
618
|
+
}
|
|
619
|
+
get state() {
|
|
620
|
+
return this.scrambleRemaining > 0 || this.active?.mode === "flywheel" ? "scrambling" : this.drag?.kind === "layer" ? "dragging" : this.active?.mode === "snap" ? "snapping" : this.active || this.queue.length ? "animating" : "idle";
|
|
621
|
+
}
|
|
622
|
+
get isHovering() {
|
|
623
|
+
return this.hovering;
|
|
624
|
+
}
|
|
625
|
+
isSolved() {
|
|
626
|
+
return this.solved;
|
|
627
|
+
}
|
|
628
|
+
on(e, t) {
|
|
629
|
+
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), () => this.listeners.get(e)?.delete(t);
|
|
630
|
+
}
|
|
631
|
+
off(e, t) {
|
|
632
|
+
this.listeners.get(e)?.delete(t);
|
|
633
|
+
}
|
|
634
|
+
emit(e, ...t) {
|
|
635
|
+
this.listeners.get(e)?.forEach((e) => {
|
|
636
|
+
try {
|
|
637
|
+
e(...t);
|
|
638
|
+
} catch (e) {
|
|
639
|
+
console.error(e);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
scramble(e = this.options.scrambleMoves) {
|
|
644
|
+
this.cancelQueue(), this.scrambleRemaining > 0 && this.finishScramble();
|
|
645
|
+
let t = C(this.options.size, e);
|
|
646
|
+
return this.physics.resetFlywheel(), this.physics.setSpinningUp(!0), this.scrambleRemaining = t.length, this.emit("scrambleStart"), new Promise((e) => {
|
|
647
|
+
this.scrambleResolve = e;
|
|
648
|
+
for (let e of t) this.queue.push({
|
|
649
|
+
turn: e,
|
|
650
|
+
mode: "flywheel",
|
|
651
|
+
duration: 0,
|
|
652
|
+
resolve: () => {}
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
move(e, t = this.options.moveDuration) {
|
|
657
|
+
let n = x(e, this.options.size);
|
|
658
|
+
return Promise.all(n.map((e) => this.turn(e, t))).then(() => void 0);
|
|
659
|
+
}
|
|
660
|
+
turn(e, t = this.options.moveDuration) {
|
|
661
|
+
return t <= 0 && !this.active ? (this.applyInstant(e), Promise.resolve()) : new Promise((n) => {
|
|
662
|
+
this.queue.push({
|
|
663
|
+
turn: e,
|
|
664
|
+
mode: "timed",
|
|
665
|
+
duration: t,
|
|
666
|
+
resolve: n
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
reset() {
|
|
671
|
+
this.cancelQueue(), this.abortActive();
|
|
672
|
+
for (let e of this.cubies) e.grid.copy(e.home), e.object.position.copy(e.home), e.object.quaternion.identity();
|
|
673
|
+
this.updateSolved(!0);
|
|
674
|
+
}
|
|
675
|
+
setHover(e) {
|
|
676
|
+
this.hovering !== e && (this.hovering = e, this.applyIdleState(), this.emit("hoverChange", e));
|
|
677
|
+
}
|
|
678
|
+
setIdle(e) {
|
|
679
|
+
this.idleEnabled = e, this.applyIdleState();
|
|
680
|
+
}
|
|
681
|
+
resetOrientation() {
|
|
682
|
+
this.cubeGroup.quaternion.identity();
|
|
683
|
+
}
|
|
684
|
+
setOptions(e) {
|
|
685
|
+
let t = this.options, n = A(e);
|
|
686
|
+
this.options = j({
|
|
687
|
+
...t,
|
|
688
|
+
...n,
|
|
689
|
+
colors: {
|
|
690
|
+
...t.colors,
|
|
691
|
+
...A(n.colors)
|
|
692
|
+
},
|
|
693
|
+
physics: {
|
|
694
|
+
...t.physics,
|
|
695
|
+
...A(n.physics)
|
|
696
|
+
},
|
|
697
|
+
interaction: {
|
|
698
|
+
...t.interaction,
|
|
699
|
+
...A(n.interaction)
|
|
700
|
+
},
|
|
701
|
+
camera: {
|
|
702
|
+
...t.camera,
|
|
703
|
+
...A(n.camera)
|
|
704
|
+
},
|
|
705
|
+
sticker: {
|
|
706
|
+
...t.sticker,
|
|
707
|
+
...A(n.sticker)
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
let r = this.options;
|
|
711
|
+
this.physics.setOptions(r.physics), this.renderer.toneMappingExposure = r.exposure, this.applyBackground(), this.camera.fov = r.camera.fov, this.camera.updateProjectionMatrix(), this.placeCamera(), this.shadowPlane && (this.shadowPlane.material.opacity = r.shadowOpacity), r.shadow !== t.shadow && this.buildShadow(), (r.size !== t.size || r.cubieGap !== t.cubieGap || r.cubieRounding !== t.cubieRounding || JSON.stringify(r.colors) !== JSON.stringify(t.colors) || JSON.stringify(r.sticker) !== JSON.stringify(t.sticker)) && (this.buildCube(), this.buildShadow()), this.applyIdleState();
|
|
712
|
+
}
|
|
713
|
+
dispose() {
|
|
714
|
+
this.disposed || (this.disposed = !0, cancelAnimationFrame(this.rafId), window.clearTimeout(this.mountTimer), window.clearTimeout(this.touchLeaveTimer), this.cancelQueue(), this.resizeObserver?.disconnect(), window.removeEventListener("resize", this.resize), this.unbindPointerEvents(), this.disposeCubies(), this.shadowPlane?.geometry.dispose(), this.shadowPlane?.material.dispose(), this.envTexture.dispose(), this.pmrem.dispose(), this.physics.dispose(), this.renderer.dispose(), this.canvas.remove(), this.listeners.clear());
|
|
715
|
+
}
|
|
716
|
+
applyBackground() {
|
|
717
|
+
let t = (this.options.background ?? "").trim();
|
|
718
|
+
t && t !== "transparent" && t !== "none" && (typeof CSS > "u" || typeof CSS.supports != "function" || CSS.supports("color", t)) ? (this.renderer.setClearColor(new e.Color(t), 1), this.canvas.style.background = "") : (this.renderer.setClearColor(0, 0), this.canvas.style.background = t && t !== "transparent" && t !== "none" ? t : "");
|
|
719
|
+
}
|
|
720
|
+
placeCamera() {
|
|
721
|
+
let { size: t, camera: n } = this.options, r = n.distance ?? 3.5 * t, i = e.MathUtils.degToRad(n.pitch), a = e.MathUtils.degToRad(n.yaw);
|
|
722
|
+
this.camera.position.set(r * Math.cos(i) * Math.sin(a), r * Math.sin(i), r * Math.cos(i) * Math.cos(a)), this.camera.lookAt(0, 0, 0);
|
|
723
|
+
}
|
|
724
|
+
buildCube() {
|
|
725
|
+
this.abortActive(), this.disposeCubies();
|
|
726
|
+
let { cubies: e, resources: t } = f(this.options);
|
|
727
|
+
this.cubies = e, this.resources = t;
|
|
728
|
+
for (let t of e) this.cubeGroup.add(t.object);
|
|
729
|
+
let n = this.options.size, r = this.keyLight.shadow.camera;
|
|
730
|
+
r.left = r.bottom = -n * 1.4, r.right = r.top = n * 1.4, r.near = 1, r.far = 40, r.updateProjectionMatrix(), this.updateSolved(!0);
|
|
731
|
+
}
|
|
732
|
+
disposeCubies() {
|
|
733
|
+
for (let e of this.cubies) this.cubeGroup.remove(e.object);
|
|
734
|
+
this.cubies = [], this.resources &&= (this.resources.geometries.forEach((e) => e.dispose()), this.resources.materials.forEach((e) => e.dispose()), void 0);
|
|
735
|
+
}
|
|
736
|
+
buildShadow() {
|
|
737
|
+
if (this.shadowPlane &&= (this.scene.remove(this.shadowPlane), this.shadowPlane.geometry.dispose(), this.shadowPlane.material.dispose(), void 0), !this.options.shadow) return;
|
|
738
|
+
let t = this.options.size, n = new e.Mesh(new e.PlaneGeometry(t * 6, t * 6), new e.ShadowMaterial({
|
|
739
|
+
opacity: this.options.shadowOpacity,
|
|
740
|
+
transparent: !0
|
|
741
|
+
}));
|
|
742
|
+
n.rotation.x = -Math.PI / 2, n.position.y = -(t / 2) * 1.05 - .35, n.receiveShadow = !0, this.scene.add(n), this.shadowPlane = n;
|
|
743
|
+
}
|
|
744
|
+
updateIdle(t) {
|
|
745
|
+
if (this.hovering || !this.idleEnabled) return;
|
|
746
|
+
let n = this.options.physics, r = new e.Quaternion().setFromAxisAngle(N.y, n.idleSpin * t), i = new e.Quaternion().setFromAxisAngle(N.x, Math.sin(this.elapsed * .7) * n.idleTumble * t);
|
|
747
|
+
this.cubeGroup.quaternion.premultiply(i).premultiply(r);
|
|
748
|
+
}
|
|
749
|
+
applyIdleState() {
|
|
750
|
+
let e = !this.hovering && this.idleEnabled;
|
|
751
|
+
this.physics.setBobbing(e), this.physics.setFloatTarget(e ? this.options.physics.floatLift : 0);
|
|
752
|
+
}
|
|
753
|
+
cubiesInLayers(e, t) {
|
|
754
|
+
return this.cubies.filter((n) => t.some((t) => Math.abs(n.grid[e] - t) < .001));
|
|
755
|
+
}
|
|
756
|
+
beginTurn(t, n, r, i, a, o) {
|
|
757
|
+
let s = new e.Group();
|
|
758
|
+
this.cubeGroup.add(s);
|
|
759
|
+
let c = this.cubiesInLayers(t, n);
|
|
760
|
+
for (let e of c) s.attach(e.object);
|
|
761
|
+
let l = {
|
|
762
|
+
axis: t,
|
|
763
|
+
layers: n,
|
|
764
|
+
mode: r,
|
|
765
|
+
pivot: s,
|
|
766
|
+
cubies: c,
|
|
767
|
+
angle: 0,
|
|
768
|
+
target: i,
|
|
769
|
+
startedAt: this.elapsed,
|
|
770
|
+
duration: a,
|
|
771
|
+
resolve: o
|
|
772
|
+
};
|
|
773
|
+
return this.active = l, l;
|
|
774
|
+
}
|
|
775
|
+
setTurnAngle(e) {
|
|
776
|
+
this.active && (this.active.angle = e, this.active.pivot.rotation.set(0, 0, 0), this.active.pivot.rotation[this.active.axis] = e);
|
|
777
|
+
}
|
|
778
|
+
completeTurn() {
|
|
779
|
+
let e = this.active;
|
|
780
|
+
if (!e) return;
|
|
781
|
+
let t = Math.round(e.angle / M);
|
|
782
|
+
this.setTurnAngle(t * M), e.pivot.updateMatrixWorld(!0);
|
|
783
|
+
for (let t of e.cubies) this.cubeGroup.attach(t.object), m(t.object.position), p(t.object.quaternion), t.grid.copy(t.object.position);
|
|
784
|
+
if (this.cubeGroup.remove(e.pivot), this.active = null, e.mode === "snap" && this.physics.endSnap(), e.resolve?.(), t !== 0) {
|
|
785
|
+
let n = {
|
|
786
|
+
axis: e.axis,
|
|
787
|
+
layers: e.layers,
|
|
788
|
+
quarterTurns: t
|
|
789
|
+
};
|
|
790
|
+
this.emit("move", n, S(n, this.options.size)), this.updateSolved();
|
|
791
|
+
}
|
|
792
|
+
e.mode === "flywheel" && (--this.scrambleRemaining, this.scrambleRemaining <= 0 && this.finishScramble());
|
|
793
|
+
}
|
|
794
|
+
abortActive() {
|
|
795
|
+
let e = this.active;
|
|
796
|
+
if (e) {
|
|
797
|
+
this.setTurnAngle(0);
|
|
798
|
+
for (let t of e.cubies) this.cubeGroup.attach(t.object), t.object.position.copy(t.grid), p(t.object.quaternion);
|
|
799
|
+
this.cubeGroup.remove(e.pivot), this.active = null, this.physics.endSnap(), e.resolve?.(), e.mode === "flywheel" && this.scrambleRemaining > 0 && this.finishScramble(), this.drag?.kind === "layer" && (this.drag = null);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
applyInstant(e) {
|
|
803
|
+
this.beginTurn(e.axis, e.layers, "timed", e.quarterTurns * M, 0), this.setTurnAngle(e.quarterTurns * M), this.completeTurn();
|
|
804
|
+
}
|
|
805
|
+
cancelQueue() {
|
|
806
|
+
let e = this.queue;
|
|
807
|
+
this.queue = [], e.forEach((e) => e.resolve()), this.scrambleRemaining > 0 && (this.active?.mode === "flywheel" ? this.scrambleRemaining = 1 : this.finishScramble());
|
|
808
|
+
}
|
|
809
|
+
finishScramble() {
|
|
810
|
+
this.scrambleRemaining = 0, this.physics.setSpinningUp(!1);
|
|
811
|
+
let e = this.scrambleResolve;
|
|
812
|
+
this.scrambleResolve = null, this.emit("scrambleEnd"), e?.();
|
|
813
|
+
}
|
|
814
|
+
updateTurn(e) {
|
|
815
|
+
if (!this.active && this.queue.length && this.drag?.kind !== "layer") {
|
|
816
|
+
let e = this.queue.shift(), t = e.turn.quarterTurns * M;
|
|
817
|
+
this.beginTurn(e.turn.axis, e.turn.layers, e.mode, t, e.duration / 1e3, e.resolve);
|
|
818
|
+
}
|
|
819
|
+
let t = this.active;
|
|
820
|
+
if (t) switch (t.mode) {
|
|
821
|
+
case "timed": {
|
|
822
|
+
let e = t.duration > 0 ? Math.min(1, (this.elapsed - t.startedAt) / t.duration) : 1, n = e < .5 ? 4 * e * e * e : 1 - (-2 * e + 2) ** 3 / 2;
|
|
823
|
+
this.setTurnAngle(t.target * n), e >= 1 && this.completeTurn();
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
case "flywheel": {
|
|
827
|
+
let n = this.scrambleRemaining - Math.abs(t.angle) / M;
|
|
828
|
+
this.physics.setSpinningUp(n > this.physics.quarterTurnsToCoast + .6);
|
|
829
|
+
let r = Math.sign(t.target), i = t.angle + r * this.physics.scrambleSpeed * e;
|
|
830
|
+
Math.abs(i) >= Math.abs(t.target) ? (this.setTurnAngle(t.target), this.completeTurn()) : this.setTurnAngle(i);
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
case "drag": break;
|
|
834
|
+
case "snap": this.setTurnAngle(this.physics.snapAngle), this.physics.snapSettled && (this.setTurnAngle(this.physics.snapTarget), this.completeTurn());
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
updateSolved(e) {
|
|
838
|
+
let t = e ?? this.computeSolved(), n = t !== this.solved;
|
|
839
|
+
this.solved = t, (n || e !== void 0) && this.emit("stateChange", t), n && t && this.emit("solved");
|
|
840
|
+
}
|
|
841
|
+
computeSolved() {
|
|
842
|
+
let t = /* @__PURE__ */ new Map(), n = new e.Vector3();
|
|
843
|
+
for (let e of this.cubies) for (let r of e.stickers) {
|
|
844
|
+
n.copy(r.localNormal).applyQuaternion(e.object.quaternion);
|
|
845
|
+
let i = `${Math.round(n.x)},${Math.round(n.y)},${Math.round(n.z)}`, a = t.get(i);
|
|
846
|
+
if (a === void 0) t.set(i, r.face);
|
|
847
|
+
else if (a !== r.face) return !1;
|
|
848
|
+
}
|
|
849
|
+
return !0;
|
|
850
|
+
}
|
|
851
|
+
bindPointerEvents() {
|
|
852
|
+
let e = this.canvas;
|
|
853
|
+
e.addEventListener("pointerdown", this.onPointerDown), e.addEventListener("pointermove", this.onPointerMove), e.addEventListener("pointerup", this.onPointerUp), e.addEventListener("pointercancel", this.onPointerUp), e.addEventListener("pointerenter", this.onPointerEnter), e.addEventListener("pointerleave", this.onPointerLeave), e.addEventListener("contextmenu", this.preventDefault);
|
|
854
|
+
}
|
|
855
|
+
unbindPointerEvents() {
|
|
856
|
+
let e = this.canvas;
|
|
857
|
+
e.removeEventListener("pointerdown", this.onPointerDown), e.removeEventListener("pointermove", this.onPointerMove), e.removeEventListener("pointerup", this.onPointerUp), e.removeEventListener("pointercancel", this.onPointerUp), e.removeEventListener("pointerenter", this.onPointerEnter), e.removeEventListener("pointerleave", this.onPointerLeave), e.removeEventListener("contextmenu", this.preventDefault);
|
|
858
|
+
}
|
|
859
|
+
pointerNdc(t) {
|
|
860
|
+
let n = this.canvas.getBoundingClientRect();
|
|
861
|
+
return new e.Vector2((t.clientX - n.left) / n.width * 2 - 1, -((t.clientY - n.top) / n.height) * 2 + 1);
|
|
862
|
+
}
|
|
863
|
+
pickCubie(e) {
|
|
864
|
+
this.raycaster.setFromCamera(this.pointerNdc(e), this.camera);
|
|
865
|
+
let t = this.raycaster.intersectObjects(this.cubies.map((e) => e.body), !1)[0];
|
|
866
|
+
if (!t || !t.face) return null;
|
|
867
|
+
let n = t.object.userData.cubie, r = t.face.normal.clone().applyQuaternion(n.object.quaternion), i = Math.abs(r.x), a = Math.abs(r.y), o = Math.abs(r.z);
|
|
868
|
+
return i >= a && i >= o ? r.set(Math.sign(r.x), 0, 0) : a >= i && a >= o ? r.set(0, Math.sign(r.y), 0) : r.set(0, 0, Math.sign(r.z)), {
|
|
869
|
+
cubie: n,
|
|
870
|
+
normal: r,
|
|
871
|
+
point: this.cubeGroup.worldToLocal(t.point.clone())
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
toScreen(t) {
|
|
875
|
+
let n = this.canvas.getBoundingClientRect(), r = this.cubeGroup.localToWorld(t.clone()).project(this.camera);
|
|
876
|
+
return new e.Vector2((r.x + 1) / 2 * n.width, (1 - r.y) / 2 * n.height);
|
|
877
|
+
}
|
|
878
|
+
decideLayerDrag(t, n) {
|
|
879
|
+
let r = t.normal, i = t.hit, a = t.cubie;
|
|
880
|
+
if (this.active) return;
|
|
881
|
+
let o = [
|
|
882
|
+
"x",
|
|
883
|
+
"y",
|
|
884
|
+
"z"
|
|
885
|
+
].filter((e) => Math.abs(r[e]) < .5).map((e) => N[e].clone()), s = this.toScreen(i), c = null;
|
|
886
|
+
for (let e of o) {
|
|
887
|
+
let t = this.toScreen(i.clone().addScaledVector(e, .5)).sub(s);
|
|
888
|
+
if (t.lengthSq() < 1e-6) continue;
|
|
889
|
+
t.normalize();
|
|
890
|
+
let r = n.clone().normalize().dot(t);
|
|
891
|
+
(!c || Math.abs(r) > Math.abs(c.dot)) && (c = {
|
|
892
|
+
t: e,
|
|
893
|
+
dir: t,
|
|
894
|
+
dot: r
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
if (!c) {
|
|
898
|
+
this.drag = null;
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
let l = new e.Vector3().crossVectors(r, c.t), u = Math.abs(l.x) > .5 ? "x" : Math.abs(l.y) > .5 ? "y" : "z", d = l[u] > 0 ? 1 : -1, f = a.grid[u];
|
|
902
|
+
t.kind = "layer", t.screenDir = c.dir, t.sign = d, this.beginTurn(u, [f], "drag", 0, 0);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/core/mount.ts
|
|
907
|
+
function F(e, t = {}) {
|
|
908
|
+
let n = typeof e == "string" ? document.querySelector(e) : e;
|
|
909
|
+
if (!n) throw Error(`rubiks-cube-3d: container not found (${String(e)})`);
|
|
910
|
+
let r = new P(n, t);
|
|
911
|
+
return {
|
|
912
|
+
engine: r,
|
|
913
|
+
scramble: (e) => r.scramble(e),
|
|
914
|
+
move: (e, t) => r.move(e, t),
|
|
915
|
+
turn: (e, t) => r.turn(e, t),
|
|
916
|
+
reset: () => r.reset(),
|
|
917
|
+
resetOrientation: () => r.resetOrientation(),
|
|
918
|
+
isSolved: () => r.isSolved(),
|
|
919
|
+
get state() {
|
|
920
|
+
return r.state;
|
|
921
|
+
},
|
|
922
|
+
setHover: (e) => r.setHover(e),
|
|
923
|
+
setIdle: (e) => r.setIdle(e),
|
|
924
|
+
setOptions: (e) => r.setOptions(e),
|
|
925
|
+
on: (e, t) => r.on(e, t),
|
|
926
|
+
destroy: () => r.dispose()
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
//#endregion
|
|
930
|
+
export { E as a, O as c, x as d, C as f, w as i, j as l, s as m, P as n, k as o, S as p, D as r, T as s, F as t, y as u };
|
|
931
|
+
|
|
932
|
+
//# sourceMappingURL=core-L1AjC-fV.js.map
|