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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nativebrands Agency
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# rubiks-cube-3d
|
|
2
|
+
|
|
3
|
+
A realistic, physics-driven, fully working 3D Rubik's cube for the web — built with **three.js** (rendering) and **Matter.js** (physics). Ships as a **React component**, a **framework-free `createRubiksCube()`**, and a **single `<script>` file** you can drop into any website.
|
|
4
|
+
|
|
5
|
+
- Real cube mechanics: 2×2 up to 7×7, every layer turns exactly like the physical puzzle, standard notation in and out (`R U R' U'`, `M`, `Rw`, `3Rw`, `2L`, `x y z`), solved detection.
|
|
6
|
+
- **On mount** it scrambles itself *slow → fast → slow*. The pacing isn't an easing curve — a Matter.js flywheel is pushed, then coasts down under air friction, and its velocity *is* the turn speed.
|
|
7
|
+
- **Mouse leave**: the cube lifts on a soft spring, bobs gently and rotates slowly to invite interaction.
|
|
8
|
+
- **Mouse enter**: the motion stops and the cube settles.
|
|
9
|
+
- **Interaction**: grab any sticker and drag — the right layer follows your finger; let go and it snaps to the nearest 90° on an under-damped spring (flick to complete a turn). Drag empty space to orbit the whole cube. Touch works too.
|
|
10
|
+
- Realistic look: rounded black plastic cubies, raised glossy stickers with bevels, clear-coat PBR materials, image-based lighting, soft contact shadow — all on a **transparent background** by default (or any colour / CSS gradient).
|
|
11
|
+
- Everything is configurable: physics, speeds, colours, sticker shape, camera, shadow, interaction.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i rubiks-cube-3d three matter-js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
(`three` and `matter-js` are regular dependencies; `react` is an optional peer dependency — the core needs no framework.)
|
|
20
|
+
|
|
21
|
+
## React
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { useRef } from 'react';
|
|
25
|
+
import { RubiksCube, type RubiksCubeRef } from 'rubiks-cube-3d';
|
|
26
|
+
|
|
27
|
+
export function Hero() {
|
|
28
|
+
const cube = useRef<RubiksCubeRef>(null);
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<RubiksCube
|
|
32
|
+
ref={cube}
|
|
33
|
+
size={3}
|
|
34
|
+
background="transparent"
|
|
35
|
+
physics={{ idleSpin: 0.3, floatLift: 0.25, maxSpeed: 12 }}
|
|
36
|
+
onSolved={() => console.log('solved!')}
|
|
37
|
+
style={{ width: 360 }}
|
|
38
|
+
/>
|
|
39
|
+
<button onClick={() => cube.current?.scramble()}>Scramble</button>
|
|
40
|
+
<button onClick={() => cube.current?.move("R U R' U'")}>Sexy move</button>
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The wrapper renders a `<div>` (default `width: 100%`, square via `aspect-ratio`). Pass `width`/`height`/`style`/`className` to size it. Every option below is a prop. Options that change geometry (`size`, `colors`, `sticker`, `cubieGap`, `cubieRounding`, `antialias`, `shadow`, `maxPixelRatio`) rebuild the cube; all others update live.
|
|
47
|
+
|
|
48
|
+
`ref` exposes: `scramble(moves?)`, `move(notation, duration?)`, `turn(turn, duration?)`, `reset()`, `resetOrientation()`, `isSolved()`, `state`, `setHover(bool)`, `setIdle(bool)`, and `engine` (the full `RubiksCubeEngine`).
|
|
49
|
+
|
|
50
|
+
## Any website (no framework)
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<div id="cube" style="width: 320px"></div>
|
|
54
|
+
<script src="https://unpkg.com/rubiks-cube-3d/dist/rubiks-cube-3d.standalone.js"></script>
|
|
55
|
+
<script>
|
|
56
|
+
const cube = RubiksCube3D.mount('#cube', { size: 3, physics: { idleSpin: 0.4 } });
|
|
57
|
+
cube.on('move', (turn, notation) => console.log(notation));
|
|
58
|
+
</script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or purely declarative — every element with `data-rubiks-cube` is mounted automatically:
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<div data-rubiks-cube data-size="4" data-background="transparent"
|
|
65
|
+
data-options='{"scrambleMoves": 30, "physics": {"floatLift": 0.3}}'
|
|
66
|
+
style="width: 280px"></div>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
(`RubiksCube3D.get(element)` returns the handle of an auto-mounted element; `RubiksCube3D.autoMount(root)` mounts elements added later.)
|
|
70
|
+
|
|
71
|
+
With a bundler but without React:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { createRubiksCube } from 'rubiks-cube-3d/core';
|
|
75
|
+
const cube = createRubiksCube(document.querySelector('#cube')!, { size: 3 });
|
|
76
|
+
await cube.scramble();
|
|
77
|
+
await cube.move("F R U R' U' F'");
|
|
78
|
+
cube.destroy();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Options
|
|
82
|
+
|
|
83
|
+
All options are optional. Defaults shown.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
{
|
|
87
|
+
size: 3, // 2..7 cubies per edge
|
|
88
|
+
background: 'transparent', // 'transparent' | any CSS colour | CSS gradient/image
|
|
89
|
+
colors: { // sticker + body colours
|
|
90
|
+
U: '#ffd60a', D: '#f7f7f7', F: '#e5352b',
|
|
91
|
+
B: '#ff7f11', L: '#3a7bd5', R: '#3cb44b', body: '#0b0b0d',
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
scrambleOnMount: true,
|
|
95
|
+
scrambleMoves: 25,
|
|
96
|
+
scrambleDelay: 350, // ms before the mount scramble starts
|
|
97
|
+
moveDuration: 260, // ms for a programmatic move()
|
|
98
|
+
idleOnMount: true, // float/spin before the first mouse-leave too
|
|
99
|
+
|
|
100
|
+
physics: {
|
|
101
|
+
// scramble pacing (Matter.js flywheel)
|
|
102
|
+
scrambleForce: 0.00066, // push while spinning up → how fast it reaches top speed
|
|
103
|
+
scrambleFriction: 0.013, // air friction → how fast it slows down at the end
|
|
104
|
+
maxSpeed: 11, // rad/s cap
|
|
105
|
+
minSpeed: 1.6, // rad/s floor (the slow start/end)
|
|
106
|
+
// idle float (soft spring + periodic force)
|
|
107
|
+
floatLift: 0.22, // how high it hovers, in cubie units
|
|
108
|
+
floatAmplitude: 0.09, // bob amplitude
|
|
109
|
+
floatFrequency: 0.45, // bob Hz
|
|
110
|
+
floatStiffness: 0.004, // spring carrying the cube up/down
|
|
111
|
+
floatDamping: 0.03,
|
|
112
|
+
idleSpin: 0.25, // rad/s around the vertical axis while idle
|
|
113
|
+
idleTumble: 0.06, // rad/s gentle tumble
|
|
114
|
+
// layer snap after a drag (under-damped spring)
|
|
115
|
+
snapStiffness: 0.03,
|
|
116
|
+
snapDamping: 0.07,
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
interaction: {
|
|
120
|
+
enabled: true,
|
|
121
|
+
dragPixelsPerQuarterTurn: 110,
|
|
122
|
+
orbit: true, // drag empty space to rotate the whole cube
|
|
123
|
+
orbitSensitivity: 0.008,
|
|
124
|
+
interruptScramble: false, // let the user grab the cube mid-scramble
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
camera: { fov: 32, pitch: 31, yaw: 38, distance: undefined /* 3.5 × size */ },
|
|
128
|
+
sticker: { inset: 0.84, cornerRadius: 0.09, height: 0.018, roughness: 0.28 },
|
|
129
|
+
|
|
130
|
+
shadow: true,
|
|
131
|
+
shadowOpacity: 0.28,
|
|
132
|
+
shadowMapSize: 2048, // lower on weak GPUs
|
|
133
|
+
cubieRounding: 0.1,
|
|
134
|
+
cubieGap: 0.035,
|
|
135
|
+
maxPixelRatio: 2,
|
|
136
|
+
antialias: true,
|
|
137
|
+
exposure: 1,
|
|
138
|
+
|
|
139
|
+
// callbacks
|
|
140
|
+
onMove: (turn, notation) => {},
|
|
141
|
+
onScrambleStart: () => {},
|
|
142
|
+
onScrambleEnd: () => {},
|
|
143
|
+
onSolved: () => {},
|
|
144
|
+
onStateChange: (solved) => {},
|
|
145
|
+
onHoverChange: (hovering) => {},
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Engine API
|
|
150
|
+
|
|
151
|
+
`RubiksCubeEngine` (what the React ref's `.engine` and the handle's `.engine` give you):
|
|
152
|
+
|
|
153
|
+
| Member | Description |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `scramble(moves?)` → `Promise` | Physics-paced scramble; resolves when the last move lands. |
|
|
156
|
+
| `move(notation, duration?)` → `Promise` | `"R U R' U'"`, slices `M E S`, wide `Rw r 3Rw`, inner `2R`, rotations `x y z`. |
|
|
157
|
+
| `turn({ axis, layers, quarterTurns }, duration?)` | Low-level turn. `layers` are grid coordinates (`-1, 0, 1` on a 3×3). `duration` 0 = instant. |
|
|
158
|
+
| `reset()` | Back to solved instantly. |
|
|
159
|
+
| `resetOrientation()` | Undo orbiting/idle spin. |
|
|
160
|
+
| `isSolved()` / `state` | `state` is `'idle' \| 'scrambling' \| 'animating' \| 'dragging' \| 'snapping'`. |
|
|
161
|
+
| `setHover(bool)` / `setIdle(bool)` | Drive the hover/idle behaviour yourself. |
|
|
162
|
+
| `setOptions(partial)` | Live update; geometry changes rebuild the cube. |
|
|
163
|
+
| `on(event, fn)` → unsubscribe | Events: `move`, `scrambleStart`, `scrambleEnd`, `solved`, `stateChange`, `hoverChange`. |
|
|
164
|
+
| `dispose()` | Remove the canvas, free GPU resources, stop the loop. |
|
|
165
|
+
|
|
166
|
+
## How the physics works
|
|
167
|
+
|
|
168
|
+
Three Matter.js bodies live in a tiny zero-gravity world:
|
|
169
|
+
|
|
170
|
+
1. **Flywheel** — while the scramble's remaining moves are more than the flywheel needs to coast down, a constant force pushes it; after that air friction bleeds off the speed. Each frame the current layer advances by `flywheel.velocity × dt`, clamped to `[minSpeed, maxSpeed]`. Result: slow → fast → slow, and the last move always lands at the slow end regardless of scramble length.
|
|
171
|
+
2. **Floater** — a mass on a soft spring whose anchor moves between 0 (hovered) and `floatLift` (idle). A periodic force at `floatFrequency` makes it bob. The cube's Y offset is the body's position.
|
|
172
|
+
3. **Snapper** — when a dragged layer is released, its angle and angular velocity are handed to a body on a stiffer, under-damped spring anchored at the nearest 90°. The layer follows the body until it settles, so it wobbles like a real cube.
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npm i
|
|
178
|
+
npm run dev # React playground with live sliders → http://localhost:5173
|
|
179
|
+
npm run build # dist/: ESM + CJS library, standalone IIFE, .d.ts
|
|
180
|
+
open demo/standalone.html # after build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("three");c=s(c,1);let l=require("three/examples/jsm/environments/RoomEnvironment.js"),u=require("matter-js");u=s(u,1);let d=require("three/examples/jsm/geometries/RoundedBoxGeometry.js");var f=1e3/60,p=6,m=6.5,h=class{constructor(e){this.accumulator=0,this.elapsed=0,this.spinningUp=!1,this.bobbing=!1,this.snapping=!1,this.opts=e,this.engine=u.default.Engine.create({gravity:{x:0,y:0,scale:0}}),this.flywheel=u.default.Bodies.circle(0,0,1,{frictionAir:e.scrambleFriction,collisionFilter:{group:-1,mask:0}}),u.default.Body.setMass(this.flywheel,1),this.floater=u.default.Bodies.circle(0,0,1,{frictionAir:.012,collisionFilter:{group:-1,mask:0}}),u.default.Body.setMass(this.floater,1),this.floatSpring=u.default.Constraint.create({pointA:{x:0,y:0},bodyB:this.floater,length:0,stiffness:e.floatStiffness,damping:e.floatDamping}),this.snapper=u.default.Bodies.circle(0,0,1,{frictionAir:0,collisionFilter:{group:-1,mask:0}}),u.default.Body.setMass(this.snapper,1),this.snapSpring=u.default.Constraint.create({pointA:{x:0,y:0},bodyB:this.snapper,length:0,stiffness:e.snapStiffness,damping:e.snapDamping}),u.default.Composite.add(this.engine.world,[this.flywheel,this.floater,this.floatSpring,this.snapper,this.snapSpring])}setOptions(e){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}resetFlywheel(){u.default.Body.setPosition(this.flywheel,{x:0,y:0}),u.default.Body.setVelocity(this.flywheel,{x:0,y:0}),this.spinningUp=!1}setSpinningUp(e){this.spinningUp=e}get scrambleSpeed(){let e=Math.abs(this.flywheel.velocity.x);return Math.min(this.opts.maxSpeed,Math.max(this.opts.minSpeed,e))}get flywheelVelocity(){return this.flywheel.velocity.x}get quarterTurnsToCoast(){let e=Math.abs(this.flywheel.velocity.x),t=Math.max(1e-4,this.opts.scrambleFriction);return Math.max(0,e-this.opts.minSpeed)/(60*t)/(Math.PI/2)}setFloatTarget(e){this.floatSpring.pointA.y=e}setBobbing(e){this.bobbing=e}get floatY(){return this.floater.position.y}beginSnap(e,t,n){u.default.Body.setPosition(this.snapper,{x:e,y:0}),u.default.Body.setVelocity(this.snapper,{x:t/60,y:0}),this.snapSpring.pointA.x=n,this.snapping=!0}endSnap(){this.snapping=!1}get snapAngle(){return this.snapper.position.x}get snapTarget(){return this.snapSpring.pointA.x}get snapSettled(){return this.snapping&&Math.abs(this.snapper.position.x-this.snapSpring.pointA.x)<.004&&Math.abs(this.snapper.velocity.x)<.004}step(e){this.accumulator+=Math.min(e,.1)*1e3;let t=0;for(;this.accumulator>=f&&t<p;)this.substep(),this.accumulator-=f,t++;t===p&&(this.accumulator=0)}substep(){this.elapsed+=f/1e3;let{opts:e}=this;if(this.spinningUp&&u.default.Body.applyForce(this.flywheel,this.flywheel.position,{x:e.scrambleForce,y:0}),u.default.Body.setPosition(this.flywheel,{x:0,y:0}),this.bobbing){let t=2*Math.PI*e.floatFrequency,n=Math.sin(this.elapsed*t)*e.floatAmplitude*e.floatStiffness*m;u.default.Body.applyForce(this.floater,this.floater.position,{x:0,y:n/277.8})}u.default.Body.setPosition(this.floater,{x:0,y:this.floater.position.y}),u.default.Engine.update(this.engine,f)}dispose(){u.default.World.clear(this.engine.world,!1),u.default.Engine.clear(this.engine)}},g={R:new c.Vector3(1,0,0),L:new c.Vector3(-1,0,0),U:new c.Vector3(0,1,0),D:new c.Vector3(0,-1,0),F:new c.Vector3(0,0,1),B:new c.Vector3(0,0,-1)};function _(e,t){let n=e/2,r=new c.Shape;return r.moveTo(-n+t,-n),r.lineTo(n-t,-n),r.quadraticCurveTo(n,-n,n,-n+t),r.lineTo(n,n-t),r.quadraticCurveTo(n,n,n-t,n),r.lineTo(-n+t,n),r.quadraticCurveTo(-n,n,-n,n-t),r.lineTo(-n,-n+t),r.quadraticCurveTo(-n,-n,-n+t,-n),r}function v(e){return new c.MeshPhysicalMaterial({color:new c.Color(e.body),roughness:.42,metalness:0,clearcoat:.55,clearcoatRoughness:.3,envMapIntensity:.9})}function y(e,t){return new c.MeshPhysicalMaterial({color:new c.Color(e),roughness:t,metalness:0,clearcoat:.9,clearcoatRoughness:.18,envMapIntensity:.9})}function b(e){let{size:t,colors:n,sticker:r}=e,i=1-e.cubieGap,a=(t-1)/2,o=[],s=[],l=new d.RoundedBoxGeometry(i,i,i,5,e.cubieRounding*i);o.push(l);let u=v(n);s.push(u);let f=.006,p=r.inset*i-2*f,m=new c.ExtrudeGeometry(_(p,r.cornerRadius*i),{depth:Math.max(.002,r.height-f),bevelEnabled:!0,bevelThickness:f,bevelSize:f,bevelSegments:2,curveSegments:6});m.computeVertexNormals(),o.push(m);let h={U:y(n.U,r.roughness),D:y(n.D,r.roughness),F:y(n.F,r.roughness),B:y(n.B,r.roughness),L:y(n.L,r.roughness),R:y(n.R,r.roughness)};s.push(...Object.values(h));let b=[],x=new c.Vector3(0,0,1);for(let e=0;e<t;e++)for(let n=0;n<t;n++)for(let r=0;r<t;r++){let t=e-a,o=n-a,s=r-a;if(Math.abs(t)!==a&&Math.abs(o)!==a&&Math.abs(s)!==a)continue;let d=new c.Group;d.position.set(t,o,s);let f=new c.Mesh(l,u);f.castShadow=!0,f.receiveShadow=!0,d.add(f);let p=[],_=[];t===a&&_.push(`R`),t===-a&&_.push(`L`),o===a&&_.push(`U`),o===-a&&_.push(`D`),s===a&&_.push(`F`),s===-a&&_.push(`B`);for(let e of _){let t=g[e],n=new c.Mesh(m,h[e]);n.quaternion.setFromUnitVectors(x,t),n.position.copy(t).multiplyScalar(i/2-.002),n.castShadow=!1,n.receiveShadow=!0,n.userData.face=e,d.add(n),p.push({face:e,localNormal:t.clone(),mesh:n})}let v={object:d,body:f,stickers:p,grid:new c.Vector3(t,o,s),home:new c.Vector3(t,o,s)};d.userData.cubie=v,f.userData.cubie=v,b.push(v)}return{cubies:b,resources:{geometries:o,materials:s}}}function x(e){let t=new c.Matrix4().makeRotationFromQuaternion(e),n=t.elements;for(let e=0;e<16;e++)n[e]=Math.round(n[e]);e.setFromRotationMatrix(t)}function S(e){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)}var C={R:{axis:`x`,sign:1,cw:-1},L:{axis:`x`,sign:-1,cw:1},U:{axis:`y`,sign:1,cw:-1},D:{axis:`y`,sign:-1,cw:1},F:{axis:`z`,sign:1,cw:-1},B:{axis:`z`,sign:-1,cw:1}},w={M:{axis:`x`,cw:1},E:{axis:`y`,cw:1},S:{axis:`z`,cw:-1}},T={x:{axis:`x`,cw:-1},y:{axis:`y`,cw:-1},z:{axis:`z`,cw:-1}},E=/^(\d*)([UDFBLRudfblrMESxyz])(w?)(['’2]*)$/;function D(e){let t=(e-1)/2,n=[];for(let r=0;r<e;r++)n.push(r-t);return n}function O(e){let t=1;for(let n of e)t*=n===`2`?2:-1;return t}function k(e,t){let n=(t-1)/2,r=[];for(let i of e.trim().split(/\s+/)){if(!i)continue;let e=E.exec(i);if(!e)throw Error(`rubiks-cube-3d: cannot parse move "${i}"`);let[,a,o,s,c]=e,l=O(c),u=a?parseInt(a,10):0;if(o===`x`||o===`y`||o===`z`){let e=T[o];r.push({axis:e.axis,layers:D(t),quarterTurns:e.cw*l});continue}if(o===`M`||o===`E`||o===`S`){let e=w[o],n=t%2==1?[0]:[-.5,.5];r.push({axis:e.axis,layers:n,quarterTurns:e.cw*l});continue}let d=o===o.toLowerCase(),f=C[o.toUpperCase()],p=d||s===`w`,m=[];if(p){let e=Math.min(t-1,Math.max(2,u||2));for(let t=0;t<e;t++)m.push(f.sign*(n-t))}else u>1?m.push(f.sign*(n-Math.min(u,t)+1)):m.push(f.sign*n);r.push({axis:f.axis,layers:m,quarterTurns:f.cw*l})}return r}function A(e,t){let n=(t-1)/2,r=(e.quarterTurns%4+4)%4;if(r===0)return``;let i=e=>{let t=e*(r===3?-1:r);return t===2||t===-2?`2`:t>0?``:`'`},a=[...e.layers].sort((e,t)=>e-t);if(a.length===t){let t=T[e.axis];return`${e.axis}${i(t.cw)}`}let o=a.find(e=>Math.abs(e)===n),s=o===void 0?a[a.length-1]>0:o>0,c=Object.keys(C).find(t=>C[t].axis===e.axis&&C[t].sign>0===s),l=C[c];if(a.length===1){let r=a[0];if(Math.abs(r)===n)return`${c}${i(l.cw)}`;if(t%2==1&&r===0){let t=e.axis===`x`?`M`:e.axis===`y`?`E`:`S`;return`${t}${i(w[t].cw)}`}return`${Math.round(n-Math.abs(r))+1}${c}${i(l.cw)}`}if(t%2==0&&a.length===2&&a[0]===-.5&&a[1]===.5){let t=e.axis===`x`?`M`:e.axis===`y`?`E`:`S`;return`${t}${i(w[t].cw)}`}let u=a.length;return`${u>2?u:``}${c}w${i(l.cw)}`}function j(e,t,n=Math.random){let r=[`x`,`y`,`z`],i=D(e),a=(e-1)/2,o=[],s=null,c=0;for(let l=0;l<t;l++){let t;do t=r[Math.floor(n()*3)];while(t===s&&c>=1&&n()<.85);c=t===s?c+1:0,s=t;let l;l=e===3||n()<.7?n()<.5?a:-a:i[Math.floor(n()*i.length)];let u=n()<.15?2:n()<.5?1:-1;o.push({axis:t,layers:[l],quarterTurns:u})}return o}var M={U:`#ffd60a`,D:`#f7f7f7`,F:`#e5352b`,B:`#ff7f11`,L:`#3a7bd5`,R:`#3cb44b`,body:`#0b0b0d`},N={scrambleForce:66e-5,scrambleFriction:.013,maxSpeed:11,minSpeed:1.6,floatLift:.22,floatAmplitude:.09,floatFrequency:.45,floatStiffness:.004,floatDamping:.03,idleSpin:.25,idleTumble:.06,snapStiffness:.03,snapDamping:.07},P={enabled:!0,dragPixelsPerQuarterTurn:110,orbit:!0,orbitSensitivity:.008,interruptScramble:!1},F={fov:32,pitch:31,yaw:38},I={inset:.84,cornerRadius:.09,height:.018,roughness:.28},L={size:3,background:`transparent`,colors:{},scrambleOnMount:!0,scrambleMoves:25,scrambleDelay:350,moveDuration:260,physics:{},interaction:{},camera:{},sticker:{},shadow:!0,shadowOpacity:.28,shadowMapSize:2048,cubieRounding:.1,cubieGap:.035,maxPixelRatio:2,antialias:!0,exposure:1,idleOnMount:!0};function R(e){let t={};if(!e)return t;for(let n of Object.keys(e))e[n]!==void 0&&(t[n]=e[n]);return t}function z(e={}){let t=R(e),n=Math.max(2,Math.min(7,Math.round(t.size??L.size)));return{...L,...t,size:n,colors:{...M,...R(t.colors)},physics:{...N,...R(t.physics)},interaction:{...P,...R(t.interaction)},camera:{...F,...R(t.camera)},sticker:{...I,...R(t.sticker)}}}var B=Math.PI/2,V={x:new c.Vector3(1,0,0),y:new c.Vector3(0,1,0),z:new c.Vector3(0,0,1)},H=class{constructor(e,t={}){this.cubeGroup=new c.Group,this.cubies=[],this.raycaster=new c.Raycaster,this.queue=[],this.active=null,this.drag=null,this.scrambleRemaining=0,this.scrambleResolve=null,this.hovering=!1,this.solved=!0,this.timer=new c.Timer,this.elapsed=0,this.rafId=0,this.disposed=!1,this.touchLeaveTimer=0,this.listeners=new Map,this.mountTimer=0,this.squareMode=!1,this.resize=()=>{let e=this.container.clientWidth||300;this.container.clientHeight||(this.squareMode=!0);let t=this.squareMode?e:this.container.clientHeight;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()},this.tick=()=>{if(this.disposed)return;this.rafId=requestAnimationFrame(this.tick),this.timer.update();let e=Math.min(this.timer.getDelta(),.1);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)},this.preventDefault=e=>e.preventDefault(),this.onPointerEnter=e=>{e.pointerType!==`touch`&&(window.clearTimeout(this.touchLeaveTimer),this.setHover(!0))},this.onPointerLeave=e=>{e.pointerType!==`touch`&&(this.drag||this.setHover(!1))},this.onPointerDown=e=>{let t=this.options.interaction;if(!t.enabled||e.pointerType===`mouse`&&e.button!==0||(e.pointerType===`touch`&&(window.clearTimeout(this.touchLeaveTimer),this.setHover(!0)),this.drag))return;let n=this.state===`scrambling`;if(n&&!t.interruptScramble)return;let r=this.pickCubie(e);if(r){if(this.active){n&&this.cancelQueue();return}this.drag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,kind:`undecided`,cubie:r.cubie,normal:r.normal,hit:r.point,samples:[]}}else if(t.orbit)this.drag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,kind:`orbit`,samples:[]};else return;this.canvas.setPointerCapture(e.pointerId),this.canvas.style.cursor=`grabbing`,e.preventDefault()},this.onPointerMove=e=>{let t=this.drag;if(!t||t.pointerId!==e.pointerId){!t&&e.pointerType===`mouse`&&(this.canvas.style.cursor=this.pickCubie(e)?`grab`:`default`);return}let n=e.clientX-t.lastX,r=e.clientY-t.lastY;if(t.lastX=e.clientX,t.lastY=e.clientY,t.kind===`orbit`){let e=this.options.interaction.orbitSensitivity,t=new c.Quaternion().setFromAxisAngle(V.y,n*e),i=new c.Quaternion().setFromAxisAngle(V.x,r*e);this.cubeGroup.quaternion.premultiply(t).premultiply(i);return}let i=e.clientX-t.startX,a=e.clientY-t.startY;if(t.kind===`undecided`&&(Math.hypot(i,a)<6||(this.decideLayerDrag(t,new c.Vector2(i,a)),t.kind!==`layer`)))return;let o=new c.Vector2(i,a).dot(t.screenDir)/this.options.interaction.dragPixelsPerQuarterTurn*B*t.sign;this.setTurnAngle(o),t.samples.push({t:performance.now(),angle:o}),t.samples.length>6&&t.samples.shift()},this.onPointerUp=e=>{let t=this.drag;if(e.pointerType===`touch`&&(window.clearTimeout(this.touchLeaveTimer),this.touchLeaveTimer=window.setTimeout(()=>this.setHover(!1),1500)),t&&t.pointerId===e.pointerId){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`){let e=this.active,n=0;if(t.samples.length>=2){let e=t.samples[0],r=t.samples[t.samples.length-1],i=r.t-e.t;i>0&&(n=(r.angle-e.angle)/i*1e3)}let r=e.angle+n*.12,i=Math.floor(e.angle/B)*B,a=Math.ceil(e.angle/B)*B,o=Math.max(i,Math.min(a,Math.round(r/B)*B));e.mode=`snap`,e.target=o,this.physics.beginSnap(e.angle,n,o)}if(e.pointerType===`mouse`){let t=this.canvas.getBoundingClientRect();e.clientX>=t.left&&e.clientX<=t.right&&e.clientY>=t.top&&e.clientY<=t.bottom||this.setHover(!1)}}},this.container=e,this.options=z(t),this.idleEnabled=this.options.idleOnMount,this.renderer=new c.WebGLRenderer({alpha:!0,antialias:this.options.antialias,powerPreference:`high-performance`,premultipliedAlpha:!0}),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=c.PCFShadowMap,this.renderer.toneMapping=c.NeutralToneMapping,this.renderer.toneMappingExposure=this.options.exposure,this.renderer.outputColorSpace=c.SRGBColorSpace,this.canvas=this.renderer.domElement,Object.assign(this.canvas.style,{display:`block`,width:`100%`,height:`100%`,touchAction:`none`,userSelect:`none`,cursor:`grab`,outline:`none`}),this.canvas.setAttribute(`aria-label`,`Interactive Rubik's cube`),this.canvas.setAttribute(`role`,`img`),e.appendChild(this.canvas),this.applyBackground(),this.scene=new c.Scene,this.pmrem=new c.PMREMGenerator(this.renderer),this.envTexture=this.pmrem.fromScene(new l.RoomEnvironment,.04).texture,this.scene.environment=this.envTexture,this.scene.environmentIntensity=.7,this.keyLight=new c.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);let n=new c.DirectionalLight(14674175,.55);n.position.set(-6,2,-4),this.scene.add(n);let r=new c.DirectionalLight(16777215,.35);r.position.set(0,-3,-6),this.scene.add(r),this.scene.add(this.cubeGroup),this.camera=new c.PerspectiveCamera(this.options.camera.fov,1,.1,100),this.placeCamera(),this.physics=new h(this.options.physics),this.buildCube(),this.buildShadow(),this.resize(),typeof ResizeObserver<`u`?(this.resizeObserver=new ResizeObserver(()=>this.resize()),this.resizeObserver.observe(e)):window.addEventListener(`resize`,this.resize),this.bindPointerEvents();for(let e of[`onMove`,`onScrambleStart`,`onScrambleEnd`,`onSolved`,`onStateChange`,`onHoverChange`]){let t=this.options[e];t&&this.on(e.slice(2).replace(/^[A-Z]/,e=>e.toLowerCase()),t)}this.applyIdleState(),this.rafId=requestAnimationFrame(this.tick),this.options.scrambleOnMount&&(this.mountTimer=window.setTimeout(()=>{this.mountTimer=0,this.scramble()},this.options.scrambleDelay))}get state(){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`}get isHovering(){return this.hovering}isSolved(){return this.solved}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.listeners.get(e)?.delete(t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,...t){this.listeners.get(e)?.forEach(e=>{try{e(...t)}catch(e){console.error(e)}})}scramble(e=this.options.scrambleMoves){this.cancelQueue(),this.scrambleRemaining>0&&this.finishScramble();let t=j(this.options.size,e);return this.physics.resetFlywheel(),this.physics.setSpinningUp(!0),this.scrambleRemaining=t.length,this.emit(`scrambleStart`),new Promise(e=>{this.scrambleResolve=e;for(let e of t)this.queue.push({turn:e,mode:`flywheel`,duration:0,resolve:()=>{}})})}move(e,t=this.options.moveDuration){let n=k(e,this.options.size);return Promise.all(n.map(e=>this.turn(e,t))).then(()=>void 0)}turn(e,t=this.options.moveDuration){return t<=0&&!this.active?(this.applyInstant(e),Promise.resolve()):new Promise(n=>{this.queue.push({turn:e,mode:`timed`,duration:t,resolve:n})})}reset(){this.cancelQueue(),this.abortActive();for(let e of this.cubies)e.grid.copy(e.home),e.object.position.copy(e.home),e.object.quaternion.identity();this.updateSolved(!0)}setHover(e){this.hovering!==e&&(this.hovering=e,this.applyIdleState(),this.emit(`hoverChange`,e))}setIdle(e){this.idleEnabled=e,this.applyIdleState()}resetOrientation(){this.cubeGroup.quaternion.identity()}setOptions(e){let t=this.options,n=R(e);this.options=z({...t,...n,colors:{...t.colors,...R(n.colors)},physics:{...t.physics,...R(n.physics)},interaction:{...t.interaction,...R(n.interaction)},camera:{...t.camera,...R(n.camera)},sticker:{...t.sticker,...R(n.sticker)}});let r=this.options;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()}dispose(){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())}applyBackground(){let e=(this.options.background??``).trim();e&&e!==`transparent`&&e!==`none`&&(typeof CSS>`u`||typeof CSS.supports!=`function`||CSS.supports(`color`,e))?(this.renderer.setClearColor(new c.Color(e),1),this.canvas.style.background=``):(this.renderer.setClearColor(0,0),this.canvas.style.background=e&&e!==`transparent`&&e!==`none`?e:``)}placeCamera(){let{size:e,camera:t}=this.options,n=t.distance??3.5*e,r=c.MathUtils.degToRad(t.pitch),i=c.MathUtils.degToRad(t.yaw);this.camera.position.set(n*Math.cos(r)*Math.sin(i),n*Math.sin(r),n*Math.cos(r)*Math.cos(i)),this.camera.lookAt(0,0,0)}buildCube(){this.abortActive(),this.disposeCubies();let{cubies:e,resources:t}=b(this.options);this.cubies=e,this.resources=t;for(let t of e)this.cubeGroup.add(t.object);let n=this.options.size,r=this.keyLight.shadow.camera;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)}disposeCubies(){for(let e of this.cubies)this.cubeGroup.remove(e.object);this.cubies=[],this.resources&&=(this.resources.geometries.forEach(e=>e.dispose()),this.resources.materials.forEach(e=>e.dispose()),void 0)}buildShadow(){if(this.shadowPlane&&=(this.scene.remove(this.shadowPlane),this.shadowPlane.geometry.dispose(),this.shadowPlane.material.dispose(),void 0),!this.options.shadow)return;let e=this.options.size,t=new c.Mesh(new c.PlaneGeometry(e*6,e*6),new c.ShadowMaterial({opacity:this.options.shadowOpacity,transparent:!0}));t.rotation.x=-Math.PI/2,t.position.y=-(e/2)*1.05-.35,t.receiveShadow=!0,this.scene.add(t),this.shadowPlane=t}updateIdle(e){if(this.hovering||!this.idleEnabled)return;let t=this.options.physics,n=new c.Quaternion().setFromAxisAngle(V.y,t.idleSpin*e),r=new c.Quaternion().setFromAxisAngle(V.x,Math.sin(this.elapsed*.7)*t.idleTumble*e);this.cubeGroup.quaternion.premultiply(r).premultiply(n)}applyIdleState(){let e=!this.hovering&&this.idleEnabled;this.physics.setBobbing(e),this.physics.setFloatTarget(e?this.options.physics.floatLift:0)}cubiesInLayers(e,t){return this.cubies.filter(n=>t.some(t=>Math.abs(n.grid[e]-t)<.001))}beginTurn(e,t,n,r,i,a){let o=new c.Group;this.cubeGroup.add(o);let s=this.cubiesInLayers(e,t);for(let e of s)o.attach(e.object);let l={axis:e,layers:t,mode:n,pivot:o,cubies:s,angle:0,target:r,startedAt:this.elapsed,duration:i,resolve:a};return this.active=l,l}setTurnAngle(e){this.active&&(this.active.angle=e,this.active.pivot.rotation.set(0,0,0),this.active.pivot.rotation[this.active.axis]=e)}completeTurn(){let e=this.active;if(!e)return;let t=Math.round(e.angle/B);this.setTurnAngle(t*B),e.pivot.updateMatrixWorld(!0);for(let t of e.cubies)this.cubeGroup.attach(t.object),S(t.object.position),x(t.object.quaternion),t.grid.copy(t.object.position);if(this.cubeGroup.remove(e.pivot),this.active=null,e.mode===`snap`&&this.physics.endSnap(),e.resolve?.(),t!==0){let n={axis:e.axis,layers:e.layers,quarterTurns:t};this.emit(`move`,n,A(n,this.options.size)),this.updateSolved()}e.mode===`flywheel`&&(--this.scrambleRemaining,this.scrambleRemaining<=0&&this.finishScramble())}abortActive(){let e=this.active;if(e){this.setTurnAngle(0);for(let t of e.cubies)this.cubeGroup.attach(t.object),t.object.position.copy(t.grid),x(t.object.quaternion);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)}}applyInstant(e){this.beginTurn(e.axis,e.layers,`timed`,e.quarterTurns*B,0),this.setTurnAngle(e.quarterTurns*B),this.completeTurn()}cancelQueue(){let e=this.queue;this.queue=[],e.forEach(e=>e.resolve()),this.scrambleRemaining>0&&(this.active?.mode===`flywheel`?this.scrambleRemaining=1:this.finishScramble())}finishScramble(){this.scrambleRemaining=0,this.physics.setSpinningUp(!1);let e=this.scrambleResolve;this.scrambleResolve=null,this.emit(`scrambleEnd`),e?.()}updateTurn(e){if(!this.active&&this.queue.length&&this.drag?.kind!==`layer`){let e=this.queue.shift(),t=e.turn.quarterTurns*B;this.beginTurn(e.turn.axis,e.turn.layers,e.mode,t,e.duration/1e3,e.resolve)}let t=this.active;if(t)switch(t.mode){case`timed`:{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;this.setTurnAngle(t.target*n),e>=1&&this.completeTurn();break}case`flywheel`:{let n=this.scrambleRemaining-Math.abs(t.angle)/B;this.physics.setSpinningUp(n>this.physics.quarterTurnsToCoast+.6);let r=Math.sign(t.target),i=t.angle+r*this.physics.scrambleSpeed*e;Math.abs(i)>=Math.abs(t.target)?(this.setTurnAngle(t.target),this.completeTurn()):this.setTurnAngle(i);break}case`drag`:break;case`snap`:this.setTurnAngle(this.physics.snapAngle),this.physics.snapSettled&&(this.setTurnAngle(this.physics.snapTarget),this.completeTurn())}}updateSolved(e){let t=e??this.computeSolved(),n=t!==this.solved;this.solved=t,(n||e!==void 0)&&this.emit(`stateChange`,t),n&&t&&this.emit(`solved`)}computeSolved(){let e=new Map,t=new c.Vector3;for(let n of this.cubies)for(let r of n.stickers){t.copy(r.localNormal).applyQuaternion(n.object.quaternion);let i=`${Math.round(t.x)},${Math.round(t.y)},${Math.round(t.z)}`,a=e.get(i);if(a===void 0)e.set(i,r.face);else if(a!==r.face)return!1}return!0}bindPointerEvents(){let e=this.canvas;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)}unbindPointerEvents(){let e=this.canvas;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)}pointerNdc(e){let t=this.canvas.getBoundingClientRect();return new c.Vector2((e.clientX-t.left)/t.width*2-1,-((e.clientY-t.top)/t.height)*2+1)}pickCubie(e){this.raycaster.setFromCamera(this.pointerNdc(e),this.camera);let t=this.raycaster.intersectObjects(this.cubies.map(e=>e.body),!1)[0];if(!t||!t.face)return null;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);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)),{cubie:n,normal:r,point:this.cubeGroup.worldToLocal(t.point.clone())}}toScreen(e){let t=this.canvas.getBoundingClientRect(),n=this.cubeGroup.localToWorld(e.clone()).project(this.camera);return new c.Vector2((n.x+1)/2*t.width,(1-n.y)/2*t.height)}decideLayerDrag(e,t){let n=e.normal,r=e.hit,i=e.cubie;if(this.active)return;let a=[`x`,`y`,`z`].filter(e=>Math.abs(n[e])<.5).map(e=>V[e].clone()),o=this.toScreen(r),s=null;for(let e of a){let n=this.toScreen(r.clone().addScaledVector(e,.5)).sub(o);if(n.lengthSq()<1e-6)continue;n.normalize();let i=t.clone().normalize().dot(n);(!s||Math.abs(i)>Math.abs(s.dot))&&(s={t:e,dir:n,dot:i})}if(!s){this.drag=null;return}let l=new c.Vector3().crossVectors(n,s.t),u=Math.abs(l.x)>.5?`x`:Math.abs(l.y)>.5?`y`:`z`,d=l[u]>0?1:-1,f=i.grid[u];e.kind=`layer`,e.screenDir=s.dir,e.sign=d,this.beginTurn(u,[f],`drag`,0,0)}};function U(e,t={}){let n=typeof e==`string`?document.querySelector(e):e;if(!n)throw Error(`rubiks-cube-3d: container not found (${String(e)})`);let r=new H(n,t);return{engine:r,scramble:e=>r.scramble(e),move:(e,t)=>r.move(e,t),turn:(e,t)=>r.turn(e,t),reset:()=>r.reset(),resetOrientation:()=>r.resetOrientation(),isSolved:()=>r.isSolved(),get state(){return r.state},setHover:e=>r.setHover(e),setIdle:e=>r.setIdle(e),setOptions:e=>r.setOptions(e),on:(e,t)=>r.on(e,t),destroy:()=>r.dispose()}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return D}});
|
|
2
|
+
//# sourceMappingURL=core-CRe_v4Mo.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-CRe_v4Mo.cjs","names":[],"sources":["../src/core/physics.ts","../src/core/geometry.ts","../src/core/notation.ts","../src/core/types.ts","../src/core/RubiksCubeEngine.ts","../src/core/mount.ts"],"sourcesContent":["import Matter from 'matter-js';\nimport type { PhysicsOptions } from './types';\n\nconst STEP_MS = 1000 / 60;\nconst MAX_STEPS_PER_FRAME = 6;\n// empirically calibrated so that `floatAmplitude` comes out in world units\nconst FLOAT_FORCE_GAIN = 6.5;\n\n/**\n * All the \"feel\" of the cube comes from a tiny Matter.js world with three bodies:\n *\n * • flywheel – a mass on a frictionless rail. While the scramble spins up we push it;\n * afterwards air friction bleeds the speed off. Its velocity IS the\n * angular speed of the current scramble turn, which is what produces the\n * slow → fast → slow envelope instead of a canned easing curve.\n * • floater – a mass on a soft spring. The anchor moves between \"resting\" and\n * \"lifted\"; a small periodic force makes it bob. Its y is the cube's\n * hover offset.\n * • snapper – a mass on a stiffer, under-damped spring. When you let go of a layer\n * mid-turn its position is the layer angle and the anchor is the nearest\n * 90°, so layers settle with a real spring wobble.\n */\nexport class CubePhysics {\n private readonly engine: Matter.Engine;\n private readonly flywheel: Matter.Body;\n private readonly floater: Matter.Body;\n private readonly floatSpring: Matter.Constraint;\n private readonly snapper: Matter.Body;\n private readonly snapSpring: Matter.Constraint;\n\n private opts: PhysicsOptions;\n private accumulator = 0;\n private elapsed = 0;\n private spinningUp = false;\n private bobbing = false;\n private snapping = false;\n\n constructor(opts: PhysicsOptions) {\n this.opts = opts;\n this.engine = Matter.Engine.create({ gravity: { x: 0, y: 0, scale: 0 } });\n\n this.flywheel = Matter.Bodies.circle(0, 0, 1, {\n frictionAir: opts.scrambleFriction,\n collisionFilter: { group: -1, mask: 0 },\n });\n Matter.Body.setMass(this.flywheel, 1);\n\n this.floater = Matter.Bodies.circle(0, 0, 1, {\n frictionAir: 0.012,\n collisionFilter: { group: -1, mask: 0 },\n });\n Matter.Body.setMass(this.floater, 1);\n this.floatSpring = Matter.Constraint.create({\n pointA: { x: 0, y: 0 },\n bodyB: this.floater,\n length: 0,\n stiffness: opts.floatStiffness,\n damping: opts.floatDamping,\n });\n\n this.snapper = Matter.Bodies.circle(0, 0, 1, {\n frictionAir: 0,\n collisionFilter: { group: -1, mask: 0 },\n });\n Matter.Body.setMass(this.snapper, 1);\n this.snapSpring = Matter.Constraint.create({\n pointA: { x: 0, y: 0 },\n bodyB: this.snapper,\n length: 0,\n stiffness: opts.snapStiffness,\n damping: opts.snapDamping,\n });\n\n Matter.Composite.add(this.engine.world, [\n this.flywheel,\n this.floater,\n this.floatSpring,\n this.snapper,\n this.snapSpring,\n ]);\n }\n\n setOptions(opts: PhysicsOptions): void {\n this.opts = opts;\n this.flywheel.frictionAir = opts.scrambleFriction;\n this.floatSpring.stiffness = opts.floatStiffness;\n this.floatSpring.damping = opts.floatDamping;\n this.snapSpring.stiffness = opts.snapStiffness;\n this.snapSpring.damping = opts.snapDamping;\n }\n\n // ───────────────────────────── flywheel (scramble pacing) ─────────────────────────────\n\n /** Reset the flywheel so the next scramble starts from the slow end. */\n resetFlywheel(): void {\n Matter.Body.setPosition(this.flywheel, { x: 0, y: 0 });\n Matter.Body.setVelocity(this.flywheel, { x: 0, y: 0 });\n this.spinningUp = false;\n }\n\n /** While true, the flywheel is pushed each step (the \"getting faster\" half of a scramble). */\n setSpinningUp(on: boolean): void {\n this.spinningUp = on;\n }\n\n /** Current scramble turn speed in rad/s, clamped to [minSpeed, maxSpeed]. */\n get scrambleSpeed(): number {\n const v = Math.abs(this.flywheel.velocity.x);\n return Math.min(this.opts.maxSpeed, Math.max(this.opts.minSpeed, v));\n }\n\n /** Raw flywheel velocity (for diagnostics / tuning). */\n get flywheelVelocity(): number {\n return this.flywheel.velocity.x;\n }\n\n /**\n * How many quarter turns the flywheel will still travel while coasting from its\n * current speed down to `minSpeed` under air friction. The scramble uses this to\n * decide when to stop pushing so the last move lands right at the slow end.\n */\n get quarterTurnsToCoast(): number {\n const v = Math.abs(this.flywheel.velocity.x);\n const f = Math.max(1e-4, this.opts.scrambleFriction);\n return Math.max(0, v - this.opts.minSpeed) / (60 * f) / (Math.PI / 2);\n }\n\n // ───────────────────────────── floater (hover / idle bob) ─────────────────────────────\n\n /** Height (world units) the float spring pulls toward. */\n setFloatTarget(y: number): void {\n this.floatSpring.pointA.y = y;\n }\n\n setBobbing(on: boolean): void {\n this.bobbing = on;\n }\n\n /** Current vertical offset of the cube. */\n get floatY(): number {\n return this.floater.position.y;\n }\n\n // ───────────────────────────── snapper (layer snap-back) ─────────────────────────────\n\n /** Start a spring snap from `angle` (with current angular velocity) toward `target`. */\n beginSnap(angle: number, velocity: number, target: number): void {\n Matter.Body.setPosition(this.snapper, { x: angle, y: 0 });\n Matter.Body.setVelocity(this.snapper, { x: velocity / 60, y: 0 });\n this.snapSpring.pointA.x = target;\n this.snapping = true;\n }\n\n endSnap(): void {\n this.snapping = false;\n }\n\n get snapAngle(): number {\n return this.snapper.position.x;\n }\n\n get snapTarget(): number {\n return this.snapSpring.pointA.x;\n }\n\n get snapSettled(): boolean {\n return (\n this.snapping &&\n Math.abs(this.snapper.position.x - this.snapSpring.pointA.x) < 0.004 &&\n Math.abs(this.snapper.velocity.x) < 0.004\n );\n }\n\n // ───────────────────────────── stepping ─────────────────────────────\n\n /** Advance the world by `dtSeconds` using fixed 60 Hz sub-steps. */\n step(dtSeconds: number): void {\n this.accumulator += Math.min(dtSeconds, 0.1) * 1000;\n let steps = 0;\n while (this.accumulator >= STEP_MS && steps < MAX_STEPS_PER_FRAME) {\n this.substep();\n this.accumulator -= STEP_MS;\n steps++;\n }\n if (steps === MAX_STEPS_PER_FRAME) this.accumulator = 0;\n }\n\n private substep(): void {\n this.elapsed += STEP_MS / 1000;\n const { opts } = this;\n\n if (this.spinningUp) {\n Matter.Body.applyForce(this.flywheel, this.flywheel.position, { x: opts.scrambleForce, y: 0 });\n }\n // keep the rail one-dimensional and the position bounded\n Matter.Body.setPosition(this.flywheel, { x: 0, y: 0 });\n\n if (this.bobbing) {\n // a gentle periodic push; amplitude is normalised against the spring so\n // `floatAmplitude` stays in world units regardless of stiffness\n const w = 2 * Math.PI * opts.floatFrequency;\n const f = Math.sin(this.elapsed * w) * opts.floatAmplitude * opts.floatStiffness * FLOAT_FORCE_GAIN;\n Matter.Body.applyForce(this.floater, this.floater.position, { x: 0, y: f / 277.8 });\n }\n Matter.Body.setPosition(this.floater, { x: 0, y: this.floater.position.y });\n\n Matter.Engine.update(this.engine, STEP_MS);\n }\n\n dispose(): void {\n Matter.World.clear(this.engine.world, false);\n Matter.Engine.clear(this.engine);\n }\n}\n","import * as THREE from 'three';\nimport { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';\nimport type { CubeColors, Face, ResolvedOptions } from './types';\n\nexport const FACE_NORMALS: Record<Face, THREE.Vector3> = {\n R: new THREE.Vector3(1, 0, 0),\n L: new THREE.Vector3(-1, 0, 0),\n U: new THREE.Vector3(0, 1, 0),\n D: new THREE.Vector3(0, -1, 0),\n F: new THREE.Vector3(0, 0, 1),\n B: new THREE.Vector3(0, 0, -1),\n};\n\nexport interface StickerInfo {\n face: Face;\n /** Outward normal in the cubie's own (unrotated) frame. */\n localNormal: THREE.Vector3;\n mesh: THREE.Mesh;\n}\n\nexport interface Cubie {\n /** Group holding the plastic body and its stickers. Lives directly under the cube group. */\n object: THREE.Group;\n body: THREE.Mesh;\n stickers: StickerInfo[];\n /** Current logical grid position (integers for odd sizes, half-integers for even). */\n grid: THREE.Vector3;\n /** Grid position at solved state. */\n home: THREE.Vector3;\n}\n\n/** Everything that has to be disposed when the cube is rebuilt. */\nexport interface CubieResources {\n geometries: THREE.BufferGeometry[];\n materials: THREE.Material[];\n}\n\nfunction roundedRectShape(w: number, r: number): THREE.Shape {\n const h = w / 2;\n const s = new THREE.Shape();\n s.moveTo(-h + r, -h);\n s.lineTo(h - r, -h);\n s.quadraticCurveTo(h, -h, h, -h + r);\n s.lineTo(h, h - r);\n s.quadraticCurveTo(h, h, h - r, h);\n s.lineTo(-h + r, h);\n s.quadraticCurveTo(-h, h, -h, h - r);\n s.lineTo(-h, -h + r);\n s.quadraticCurveTo(-h, -h, -h + r, -h);\n return s;\n}\n\nexport function makeBodyMaterial(colors: CubeColors): THREE.MeshPhysicalMaterial {\n return new THREE.MeshPhysicalMaterial({\n color: new THREE.Color(colors.body),\n roughness: 0.42,\n metalness: 0,\n clearcoat: 0.55,\n clearcoatRoughness: 0.3,\n envMapIntensity: 0.9,\n });\n}\n\nexport function makeStickerMaterial(color: string, roughness: number): THREE.MeshPhysicalMaterial {\n return new THREE.MeshPhysicalMaterial({\n color: new THREE.Color(color),\n roughness,\n metalness: 0,\n clearcoat: 0.9,\n clearcoatRoughness: 0.18,\n envMapIntensity: 0.9,\n });\n}\n\n/**\n * Build the `size³ − (size−2)³` visible cubies. Each cubie is a rounded black box\n * with slightly raised, rounded, glossy stickers on its exposed faces.\n */\nexport function buildCubies(opts: ResolvedOptions): { cubies: Cubie[]; resources: CubieResources } {\n const { size, colors, sticker } = opts;\n const bodySize = 1 - opts.cubieGap;\n const half = (size - 1) / 2;\n\n const geometries: THREE.BufferGeometry[] = [];\n const materials: THREE.Material[] = [];\n\n const bodyGeometry = new RoundedBoxGeometry(bodySize, bodySize, bodySize, 5, opts.cubieRounding * bodySize);\n geometries.push(bodyGeometry);\n const bodyMaterial = makeBodyMaterial(colors);\n materials.push(bodyMaterial);\n\n // sticker: a rounded rectangle extruded a hair, with a soft bevel so it catches light\n const bevel = 0.006;\n const stickerWidth = sticker.inset * bodySize - 2 * bevel;\n const stickerGeometry = new THREE.ExtrudeGeometry(\n roundedRectShape(stickerWidth, sticker.cornerRadius * bodySize),\n {\n depth: Math.max(0.002, sticker.height - bevel),\n bevelEnabled: true,\n bevelThickness: bevel,\n bevelSize: bevel,\n bevelSegments: 2,\n curveSegments: 6,\n },\n );\n stickerGeometry.computeVertexNormals();\n geometries.push(stickerGeometry);\n\n const stickerMaterials: Record<Face, THREE.MeshPhysicalMaterial> = {\n U: makeStickerMaterial(colors.U, sticker.roughness),\n D: makeStickerMaterial(colors.D, sticker.roughness),\n F: makeStickerMaterial(colors.F, sticker.roughness),\n B: makeStickerMaterial(colors.B, sticker.roughness),\n L: makeStickerMaterial(colors.L, sticker.roughness),\n R: makeStickerMaterial(colors.R, sticker.roughness),\n };\n materials.push(...Object.values(stickerMaterials));\n\n const cubies: Cubie[] = [];\n const zAxis = new THREE.Vector3(0, 0, 1);\n\n for (let ix = 0; ix < size; ix++) {\n for (let iy = 0; iy < size; iy++) {\n for (let iz = 0; iz < size; iz++) {\n const x = ix - half;\n const y = iy - half;\n const z = iz - half;\n const onSurface =\n Math.abs(x) === half || Math.abs(y) === half || Math.abs(z) === half;\n if (!onSurface) continue; // hidden core cubies\n\n const group = new THREE.Group();\n group.position.set(x, y, z);\n\n const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n body.castShadow = true;\n body.receiveShadow = true;\n group.add(body);\n\n const stickers: StickerInfo[] = [];\n const faces: Face[] = [];\n if (x === half) faces.push('R');\n if (x === -half) faces.push('L');\n if (y === half) faces.push('U');\n if (y === -half) faces.push('D');\n if (z === half) faces.push('F');\n if (z === -half) faces.push('B');\n\n for (const face of faces) {\n const n = FACE_NORMALS[face];\n const mesh = new THREE.Mesh(stickerGeometry, stickerMaterials[face]);\n mesh.quaternion.setFromUnitVectors(zAxis, n);\n mesh.position.copy(n).multiplyScalar(bodySize / 2 - 0.002);\n mesh.castShadow = false;\n mesh.receiveShadow = true;\n mesh.userData.face = face;\n group.add(mesh);\n stickers.push({ face, localNormal: n.clone(), mesh });\n }\n\n const cubie: Cubie = {\n object: group,\n body,\n stickers,\n grid: new THREE.Vector3(x, y, z),\n home: new THREE.Vector3(x, y, z),\n };\n group.userData.cubie = cubie;\n body.userData.cubie = cubie;\n cubies.push(cubie);\n }\n }\n }\n\n return { cubies, resources: { geometries, materials } };\n}\n\n/** Round a quaternion to the nearest of the 24 cube orientations (kills drift after a turn). */\nexport function snapQuaternion(q: THREE.Quaternion): void {\n const m = new THREE.Matrix4().makeRotationFromQuaternion(q);\n const e = m.elements;\n for (let i = 0; i < 16; i++) e[i] = Math.round(e[i]);\n q.setFromRotationMatrix(m);\n}\n\n/** Snap a grid coordinate to the nearest half-integer (covers odd and even sizes). */\nexport function snapGrid(v: THREE.Vector3): void {\n v.x = Math.round(v.x * 2) / 2;\n v.y = Math.round(v.y * 2) / 2;\n v.z = Math.round(v.z * 2) / 2;\n // normalise -0 → 0\n if (Object.is(v.x, -0)) v.x = 0;\n if (Object.is(v.y, -0)) v.y = 0;\n if (Object.is(v.z, -0)) v.z = 0;\n}\n","import type { Axis, Face, Turn } from './types';\n\n/**\n * Standard notation → turns.\n *\n * Supports face moves (R U F L D B), slices (M E S), wide moves (Rw / r / 3Rw),\n * numbered inner layers on big cubes (2R, 3L), whole-cube rotations (x y z),\n * and the ' / 2 suffixes. Tokens are whitespace-separated: \"R U R' U' 2F2 Rw\".\n *\n * Conventions: a face move is clockwise when looking at that face, which in\n * right-hand-rule terms is −90° about +x for R, +90° about +x for L, etc.\n */\n\nconst FACE_AXIS: Record<Face, { axis: Axis; sign: 1 | -1; cw: 1 | -1 }> = {\n R: { axis: 'x', sign: 1, cw: -1 },\n L: { axis: 'x', sign: -1, cw: 1 },\n U: { axis: 'y', sign: 1, cw: -1 },\n D: { axis: 'y', sign: -1, cw: 1 },\n F: { axis: 'z', sign: 1, cw: -1 },\n B: { axis: 'z', sign: -1, cw: 1 },\n};\n\nconst SLICE: Record<'M' | 'E' | 'S', { axis: Axis; cw: 1 | -1 }> = {\n M: { axis: 'x', cw: 1 }, // follows L\n E: { axis: 'y', cw: 1 }, // follows D\n S: { axis: 'z', cw: -1 }, // follows F\n};\n\nconst ROTATION: Record<'x' | 'y' | 'z', { axis: Axis; cw: 1 | -1 }> = {\n x: { axis: 'x', cw: -1 }, // like R\n y: { axis: 'y', cw: -1 }, // like U\n z: { axis: 'z', cw: -1 }, // like F\n};\n\nconst TOKEN = /^(\\d*)([UDFBLRudfblrMESxyz])(w?)(['’2]*)$/;\n\nexport function layerCoordinates(size: number): number[] {\n const half = (size - 1) / 2;\n const out: number[] = [];\n for (let i = 0; i < size; i++) out.push(i - half);\n return out;\n}\n\nfunction suffixTurns(suffix: string): number {\n let n = 1;\n for (const ch of suffix) {\n if (ch === '2') n *= 2;\n else n *= -1;\n }\n return n;\n}\n\nexport function parseNotation(notation: string, size: number): Turn[] {\n const half = (size - 1) / 2;\n const turns: Turn[] = [];\n for (const raw of notation.trim().split(/\\s+/)) {\n if (!raw) continue;\n const m = TOKEN.exec(raw);\n if (!m) throw new Error(`rubiks-cube-3d: cannot parse move \"${raw}\"`);\n const [, numStr, letterRaw, wideFlag, suffix] = m;\n const count = suffixTurns(suffix);\n const num = numStr ? parseInt(numStr, 10) : 0;\n\n if (letterRaw === 'x' || letterRaw === 'y' || letterRaw === 'z') {\n const r = ROTATION[letterRaw];\n turns.push({ axis: r.axis, layers: layerCoordinates(size), quarterTurns: r.cw * count });\n continue;\n }\n if (letterRaw === 'M' || letterRaw === 'E' || letterRaw === 'S') {\n const s = SLICE[letterRaw];\n const layers = size % 2 === 1 ? [0] : [-0.5, 0.5];\n turns.push({ axis: s.axis, layers, quarterTurns: s.cw * count });\n continue;\n }\n\n const isLower = letterRaw === letterRaw.toLowerCase();\n const face = letterRaw.toUpperCase() as Face;\n const f = FACE_AXIS[face];\n const wide = isLower || wideFlag === 'w';\n const layers: number[] = [];\n if (wide) {\n const depth = Math.min(size - 1, Math.max(2, num || 2));\n for (let d = 0; d < depth; d++) layers.push(f.sign * (half - d));\n } else if (num > 1) {\n layers.push(f.sign * (half - Math.min(num, size) + 1));\n } else {\n layers.push(f.sign * half);\n }\n turns.push({ axis: f.axis, layers, quarterTurns: f.cw * count });\n }\n return turns;\n}\n\n/** Turn → notation string (inverse of parseNotation for common cases). */\nexport function turnToNotation(turn: Turn, size: number): string {\n const half = (size - 1) / 2;\n const q = ((turn.quarterTurns % 4) + 4) % 4; // 0..3\n if (q === 0) return '';\n const suffixFor = (cw: 1 | -1): string => {\n const signed = cw * ((q === 3 ? -1 : q) as number); // 1, 2, or -1 in the cw frame\n if (signed === 2 || signed === -2) return '2';\n return signed > 0 ? '' : \"'\";\n };\n\n const layers = [...turn.layers].sort((a, b) => a - b);\n const all = layers.length === size;\n if (all) {\n const r = ROTATION[turn.axis];\n return `${turn.axis}${suffixFor(r.cw)}`;\n }\n\n const outer = layers.find((l) => Math.abs(l) === half);\n const positive = outer !== undefined ? outer > 0 : layers[layers.length - 1] > 0;\n const face = (Object.keys(FACE_AXIS) as Face[]).find(\n (k) => FACE_AXIS[k].axis === turn.axis && (FACE_AXIS[k].sign > 0) === positive,\n )!;\n const f = FACE_AXIS[face];\n\n if (layers.length === 1) {\n const l = layers[0];\n if (Math.abs(l) === half) return `${face}${suffixFor(f.cw)}`;\n if (size % 2 === 1 && l === 0) {\n const sliceName = turn.axis === 'x' ? 'M' : turn.axis === 'y' ? 'E' : 'S';\n return `${sliceName}${suffixFor(SLICE[sliceName].cw)}`;\n }\n const depth = Math.round(half - Math.abs(l)) + 1;\n return `${depth}${face}${suffixFor(f.cw)}`;\n }\n if (size % 2 === 0 && layers.length === 2 && layers[0] === -0.5 && layers[1] === 0.5) {\n const sliceName = turn.axis === 'x' ? 'M' : turn.axis === 'y' ? 'E' : 'S';\n return `${sliceName}${suffixFor(SLICE[sliceName].cw)}`;\n }\n const depth = layers.length;\n return `${depth > 2 ? depth : ''}${face}w${suffixFor(f.cw)}`;\n}\n\n/** Random scramble: no two consecutive moves on the same axis+layer, no immediate undo. */\nexport function randomScramble(size: number, moves: number, rng: () => number = Math.random): Turn[] {\n const axes: Axis[] = ['x', 'y', 'z'];\n const coords = layerCoordinates(size);\n const half = (size - 1) / 2;\n const out: Turn[] = [];\n let lastAxis: Axis | null = null;\n let sameAxisRun = 0;\n for (let i = 0; i < moves; i++) {\n let axis: Axis;\n do {\n axis = axes[Math.floor(rng() * 3)];\n } while (axis === lastAxis && sameAxisRun >= 1 && rng() < 0.85);\n sameAxisRun = axis === lastAxis ? sameAxisRun + 1 : 0;\n lastAxis = axis;\n\n // prefer outer layers so the scramble reads like a real one\n let layer: number;\n if (size === 3 || rng() < 0.7) {\n layer = rng() < 0.5 ? half : -half;\n } else {\n layer = coords[Math.floor(rng() * coords.length)];\n }\n const quarterTurns = rng() < 0.15 ? 2 : rng() < 0.5 ? 1 : -1;\n out.push({ axis, layers: [layer], quarterTurns });\n }\n return out;\n}\n","/** Axis of rotation in the cube's own coordinate frame. */\nexport type Axis = 'x' | 'y' | 'z';\n\n/** Standard face names. U=up(+y) D=down(-y) F=front(+z) B=back(-z) L=left(-x) R=right(+x) */\nexport type Face = 'U' | 'D' | 'F' | 'B' | 'L' | 'R';\n\nexport interface CubeColors {\n U: string;\n D: string;\n F: string;\n B: string;\n L: string;\n R: string;\n /** Plastic body colour. */\n body: string;\n}\n\n/**\n * A turn of one or more layers around one axis.\n * `layers` are grid coordinates along `axis` (e.g. -1, 0, 1 on a 3x3; ±0.5 on a 2x2).\n * A face move has one layer, a wide move two, a whole-cube rotation all of them.\n */\nexport interface Turn {\n axis: Axis;\n layers: number[];\n /** Signed quarter turns. +1 = 90° counter-clockwise around the +axis (right-hand rule). */\n quarterTurns: number;\n}\n\nexport interface PhysicsOptions {\n /**\n * Force applied to the scramble \"flywheel\" while it spins up.\n * Bigger = the scramble reaches top speed sooner. Default 0.00066.\n */\n scrambleForce: number;\n /** Air friction on the flywheel (0..1). Controls how fast the scramble slows down. Default 0.013. */\n scrambleFriction: number;\n /** Top angular speed of a scramble turn, rad/s. Default 11. */\n maxSpeed: number;\n /** Minimum angular speed of a scramble turn, rad/s (the \"slow\" ends). Default 1.6. */\n minSpeed: number;\n /** How high the cube lifts while idle, in world units (a cubie is 1 unit). Default 0.22. */\n floatLift: number;\n /** Amplitude of the idle bobbing, world units. Default 0.09. */\n floatAmplitude: number;\n /** Bobbing frequency, Hz. Default 0.45. */\n floatFrequency: number;\n /** Stiffness (0..1) of the spring that carries the cube up/down between idle and hover. Default 0.004. */\n floatStiffness: number;\n /** Damping (0..1) of that spring. Default 0.03. */\n floatDamping: number;\n /** Idle spin speed around the vertical axis, rad/s. Default 0.25. */\n idleSpin: number;\n /** Extra idle tumble around the horizontal axis, rad/s. Default 0.06. */\n idleTumble: number;\n /** Stiffness (0..1) of the spring that snaps a dragged layer to the nearest 90°. Default 0.03. */\n snapStiffness: number;\n /** Damping (0..1) of the snap spring. Lower = more wobble. Default 0.07. */\n snapDamping: number;\n}\n\nexport interface InteractionOptions {\n /** Master switch for pointer interaction. Default true. */\n enabled: boolean;\n /** Pixels of drag for one quarter turn of a layer. Default 110. */\n dragPixelsPerQuarterTurn: number;\n /** Allow dragging on empty space to orbit the whole cube. Default true. */\n orbit: boolean;\n /** Orbit sensitivity, radians per pixel. Default 0.008. */\n orbitSensitivity: number;\n /** Allow the user to interrupt a running scramble by grabbing the cube. Default false. */\n interruptScramble: boolean;\n}\n\nexport interface CameraOptions {\n /** Distance from the cube centre, in cubie units. Default 3.5 * size. */\n distance?: number;\n /** Vertical field of view in degrees. Default 32. */\n fov: number;\n /** Camera elevation above the horizon, degrees. Default 31. */\n pitch: number;\n /** Camera rotation around the vertical axis, degrees. Default 38. */\n yaw: number;\n}\n\nexport interface StickerOptions {\n /** Sticker width as a fraction of the cubie. Default 0.84. */\n inset: number;\n /** Corner radius as a fraction of the cubie. Default 0.09. */\n cornerRadius: number;\n /** Sticker relief height. Default 0.018. */\n height: number;\n /** Roughness of the sticker vinyl (0 = mirror). Default 0.28. */\n roughness: number;\n}\n\nexport interface RubiksCubeOptions {\n /** Cubies per edge. 2..7 supported. Default 3. */\n size: number;\n /** Canvas background: 'transparent' (default), any CSS colour, or a CSS gradient/image. */\n background: string;\n colors: Partial<CubeColors>;\n /** Scramble the cube as soon as it is mounted. Default true. */\n scrambleOnMount: boolean;\n /** Number of random moves in a scramble. Default 25. */\n scrambleMoves: number;\n /** Delay before the mount scramble starts, ms. Default 350. */\n scrambleDelay: number;\n /** Duration of a programmatic `move()` in ms. Default 260. */\n moveDuration: number;\n physics: Partial<PhysicsOptions>;\n interaction: Partial<InteractionOptions>;\n camera: Partial<CameraOptions>;\n sticker: Partial<StickerOptions>;\n /** Soft contact shadow underneath the cube. Default true. */\n shadow: boolean;\n /** Shadow opacity 0..1. Default 0.28. */\n shadowOpacity: number;\n /** Shadow map resolution (power of two). Lower it on weak GPUs. Default 2048. */\n shadowMapSize: number;\n /** Cubie corner rounding, as a fraction of a cubie. Default 0.1. */\n cubieRounding: number;\n /** Gap between cubies, as a fraction of a cubie. Default 0.035. */\n cubieGap: number;\n /** Cap on device pixel ratio. Default 2. */\n maxPixelRatio: number;\n antialias: boolean;\n /** Overall brightness of the reflections/lighting. Default 1. */\n exposure: number;\n /** Start floating/spinning even before the first mouse-leave. Default true. */\n idleOnMount: boolean;\n /** Fired after every completed turn (user drag, scramble, or API). */\n onMove?: (turn: Turn, notation: string) => void;\n onScrambleStart?: () => void;\n onScrambleEnd?: () => void;\n /** Fired when the cube becomes solved after a turn. */\n onSolved?: () => void;\n /** Fired when the cube state changes; `solved` is the current state. */\n onStateChange?: (solved: boolean) => void;\n onHoverChange?: (hovering: boolean) => void;\n}\n\nexport const DEFAULT_COLORS: CubeColors = {\n U: '#ffd60a', // yellow\n D: '#f7f7f7', // white\n F: '#e5352b', // red\n B: '#ff7f11', // orange\n L: '#3a7bd5', // blue\n R: '#3cb44b', // green\n body: '#0b0b0d',\n};\n\nexport const DEFAULT_PHYSICS: PhysicsOptions = {\n scrambleForce: 0.00066,\n scrambleFriction: 0.013,\n maxSpeed: 11,\n minSpeed: 1.6,\n floatLift: 0.22,\n floatAmplitude: 0.09,\n floatFrequency: 0.45,\n floatStiffness: 0.004,\n floatDamping: 0.03,\n idleSpin: 0.25,\n idleTumble: 0.06,\n snapStiffness: 0.03,\n snapDamping: 0.07,\n};\n\nexport const DEFAULT_INTERACTION: InteractionOptions = {\n enabled: true,\n dragPixelsPerQuarterTurn: 110,\n orbit: true,\n orbitSensitivity: 0.008,\n interruptScramble: false,\n};\n\nexport const DEFAULT_CAMERA: CameraOptions = {\n fov: 32,\n pitch: 31,\n yaw: 38,\n};\n\nexport const DEFAULT_STICKER: StickerOptions = {\n inset: 0.84,\n cornerRadius: 0.09,\n height: 0.018,\n roughness: 0.28,\n};\n\nexport const DEFAULT_OPTIONS: RubiksCubeOptions = {\n size: 3,\n background: 'transparent',\n colors: {},\n scrambleOnMount: true,\n scrambleMoves: 25,\n scrambleDelay: 350,\n moveDuration: 260,\n physics: {},\n interaction: {},\n camera: {},\n sticker: {},\n shadow: true,\n shadowOpacity: 0.28,\n shadowMapSize: 2048,\n cubieRounding: 0.1,\n cubieGap: 0.035,\n maxPixelRatio: 2,\n antialias: true,\n exposure: 1,\n idleOnMount: true,\n};\n\n/** Fully resolved options with every default applied. */\nexport interface ResolvedOptions extends Omit<RubiksCubeOptions, 'colors' | 'physics' | 'interaction' | 'camera' | 'sticker'> {\n colors: CubeColors;\n physics: PhysicsOptions;\n interaction: InteractionOptions;\n camera: CameraOptions;\n sticker: StickerOptions;\n}\n\n/** Drop `undefined` values so they never override a default (React passes unset props as undefined). */\nexport function compact<T extends object>(obj: T | undefined): Partial<T> {\n const out: Partial<T> = {};\n if (!obj) return out;\n for (const k of Object.keys(obj) as (keyof T)[]) {\n if (obj[k] !== undefined) out[k] = obj[k];\n }\n return out;\n}\n\nexport function resolveOptions(user: Partial<RubiksCubeOptions> = {}): ResolvedOptions {\n const u = compact(user);\n const size = Math.max(2, Math.min(7, Math.round(u.size ?? DEFAULT_OPTIONS.size)));\n return {\n ...DEFAULT_OPTIONS,\n ...u,\n size,\n colors: { ...DEFAULT_COLORS, ...compact(u.colors) },\n physics: { ...DEFAULT_PHYSICS, ...compact(u.physics) },\n interaction: { ...DEFAULT_INTERACTION, ...compact(u.interaction) },\n camera: { ...DEFAULT_CAMERA, ...compact(u.camera) },\n sticker: { ...DEFAULT_STICKER, ...compact(u.sticker) },\n };\n}\n","import * as THREE from 'three';\nimport { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';\nimport { CubePhysics } from './physics';\nimport { buildCubies, snapGrid, snapQuaternion, type Cubie, type CubieResources } from './geometry';\nimport { parseNotation, randomScramble, turnToNotation } from './notation';\nimport { compact, resolveOptions, type Axis, type ResolvedOptions, type RubiksCubeOptions, type Turn } from './types';\n\nconst QUARTER = Math.PI / 2;\nconst AXIS_VEC: Record<Axis, THREE.Vector3> = {\n x: new THREE.Vector3(1, 0, 0),\n y: new THREE.Vector3(0, 1, 0),\n z: new THREE.Vector3(0, 0, 1),\n};\n\nexport type CubeState = 'idle' | 'scrambling' | 'animating' | 'dragging' | 'snapping';\n\ntype TurnMode = 'timed' | 'flywheel' | 'drag' | 'snap';\n\ninterface ActiveTurn {\n axis: Axis;\n layers: number[];\n mode: TurnMode;\n pivot: THREE.Group;\n cubies: Cubie[];\n angle: number;\n /** Target angle (multiple of π/2) for timed/flywheel/snap modes. */\n target: number;\n startedAt: number;\n duration: number;\n resolve?: () => void;\n}\n\ninterface QueuedTurn {\n turn: Turn;\n mode: 'timed' | 'flywheel';\n duration: number;\n resolve: () => void;\n}\n\ninterface DragSession {\n pointerId: number;\n startX: number;\n startY: number;\n lastX: number;\n lastY: number;\n kind: 'undecided' | 'layer' | 'orbit';\n cubie?: Cubie;\n /** Face normal of the grabbed sticker, in cube frame. */\n normal?: THREE.Vector3;\n /** Hit point in cube frame. */\n hit?: THREE.Vector3;\n /** Screen-space direction (px) of the chosen tangent. */\n screenDir?: THREE.Vector2;\n sign?: number;\n samples: { t: number; angle: number }[];\n}\n\nexport type CubeEvent = 'move' | 'scrambleStart' | 'scrambleEnd' | 'solved' | 'stateChange' | 'hoverChange';\ntype Listener = (...args: unknown[]) => void;\n\n/**\n * Framework-agnostic Rubik's cube renderer + mechanics. Mount it on any element:\n *\n * const cube = new RubiksCubeEngine(document.querySelector('#cube'), { size: 3 });\n * cube.move(\"R U R' U'\");\n * cube.dispose();\n */\nexport class RubiksCubeEngine {\n readonly container: HTMLElement;\n readonly canvas: HTMLCanvasElement;\n options: ResolvedOptions;\n\n private renderer: THREE.WebGLRenderer;\n private scene: THREE.Scene;\n private camera: THREE.PerspectiveCamera;\n private pmrem: THREE.PMREMGenerator;\n private envTexture: THREE.Texture;\n private keyLight: THREE.DirectionalLight;\n private shadowPlane?: THREE.Mesh<THREE.PlaneGeometry, THREE.ShadowMaterial>;\n\n /** Floats, orbits and idle-spins. Everything cube-shaped lives inside. */\n private cubeGroup = new THREE.Group();\n private cubies: Cubie[] = [];\n private resources?: CubieResources;\n\n private physics: CubePhysics;\n private raycaster = new THREE.Raycaster();\n\n private queue: QueuedTurn[] = [];\n private active: ActiveTurn | null = null;\n private drag: DragSession | null = null;\n private scrambleRemaining = 0;\n private scrambleResolve: (() => void) | null = null;\n\n private hovering = false;\n private idleEnabled: boolean;\n private solved = true;\n private timer = new THREE.Timer();\n private elapsed = 0;\n private rafId = 0;\n private disposed = false;\n private resizeObserver?: ResizeObserver;\n private touchLeaveTimer = 0;\n private listeners = new Map<CubeEvent, Set<Listener>>();\n private mountTimer = 0;\n\n constructor(container: HTMLElement, options: Partial<RubiksCubeOptions> = {}) {\n this.container = container;\n this.options = resolveOptions(options);\n this.idleEnabled = this.options.idleOnMount;\n\n // ── renderer ──────────────────────────────────────────────────────────\n this.renderer = new THREE.WebGLRenderer({\n alpha: true,\n antialias: this.options.antialias,\n powerPreference: 'high-performance',\n premultipliedAlpha: true,\n });\n this.renderer.shadowMap.enabled = true;\n this.renderer.shadowMap.type = THREE.PCFShadowMap;\n this.renderer.toneMapping = THREE.NeutralToneMapping;\n this.renderer.toneMappingExposure = this.options.exposure;\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n this.canvas = this.renderer.domElement;\n Object.assign(this.canvas.style, {\n display: 'block',\n width: '100%',\n height: '100%',\n touchAction: 'none',\n userSelect: 'none',\n cursor: 'grab',\n outline: 'none',\n } as Partial<CSSStyleDeclaration>);\n this.canvas.setAttribute('aria-label', \"Interactive Rubik's cube\");\n this.canvas.setAttribute('role', 'img');\n container.appendChild(this.canvas);\n this.applyBackground();\n\n // ── scene ─────────────────────────────────────────────────────────────\n this.scene = new THREE.Scene();\n this.pmrem = new THREE.PMREMGenerator(this.renderer);\n this.envTexture = this.pmrem.fromScene(new RoomEnvironment(), 0.04).texture;\n this.scene.environment = this.envTexture;\n this.scene.environmentIntensity = 0.7;\n\n this.keyLight = new THREE.DirectionalLight(0xffffff, 2.4);\n this.keyLight.position.set(4, 9, 6);\n this.keyLight.castShadow = true;\n this.keyLight.shadow.mapSize.set(this.options.shadowMapSize, this.options.shadowMapSize);\n this.keyLight.shadow.bias = -0.0004;\n this.keyLight.shadow.normalBias = 0.02;\n this.keyLight.shadow.radius = 4;\n this.scene.add(this.keyLight);\n this.scene.add(this.keyLight.target);\n\n const fill = new THREE.DirectionalLight(0xdfe8ff, 0.55);\n fill.position.set(-6, 2, -4);\n this.scene.add(fill);\n const rim = new THREE.DirectionalLight(0xffffff, 0.35);\n rim.position.set(0, -3, -6);\n this.scene.add(rim);\n\n this.scene.add(this.cubeGroup);\n\n this.camera = new THREE.PerspectiveCamera(this.options.camera.fov, 1, 0.1, 100);\n this.placeCamera();\n\n this.physics = new CubePhysics(this.options.physics);\n\n this.buildCube();\n this.buildShadow();\n\n // ── sizing & events ───────────────────────────────────────────────────\n this.resize();\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.resize());\n this.resizeObserver.observe(container);\n } else {\n window.addEventListener('resize', this.resize);\n }\n this.bindPointerEvents();\n\n for (const cb of ['onMove', 'onScrambleStart', 'onScrambleEnd', 'onSolved', 'onStateChange', 'onHoverChange'] as const) {\n const fn = this.options[cb];\n if (fn) this.on(cb.slice(2).replace(/^[A-Z]/, (c) => c.toLowerCase()) as CubeEvent, fn as Listener);\n }\n\n this.applyIdleState();\n this.rafId = requestAnimationFrame(this.tick);\n\n if (this.options.scrambleOnMount) {\n this.mountTimer = window.setTimeout(() => {\n this.mountTimer = 0;\n void this.scramble();\n }, this.options.scrambleDelay);\n }\n }\n\n // ═══════════════════════════════ public API ═══════════════════════════════\n\n get state(): CubeState {\n if (this.scrambleRemaining > 0 || (this.active?.mode === 'flywheel')) return 'scrambling';\n if (this.drag?.kind === 'layer') return 'dragging';\n if (this.active?.mode === 'snap') return 'snapping';\n if (this.active || this.queue.length) return 'animating';\n return 'idle';\n }\n\n get isHovering(): boolean {\n return this.hovering;\n }\n\n isSolved(): boolean {\n return this.solved;\n }\n\n on(event: CubeEvent, listener: Listener): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(listener);\n return () => this.listeners.get(event)?.delete(listener);\n }\n\n off(event: CubeEvent, listener: Listener): void {\n this.listeners.get(event)?.delete(listener);\n }\n\n private emit(event: CubeEvent, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach((l) => {\n try {\n l(...args);\n } catch (err) {\n console.error(err);\n }\n });\n }\n\n /**\n * Scramble with the physics flywheel: slow → fast → slow.\n * Resolves when the last move has landed.\n */\n scramble(moves = this.options.scrambleMoves): Promise<void> {\n this.cancelQueue();\n if (this.scrambleRemaining > 0) this.finishScramble(); // a previous scramble was mid-move\n const turns = randomScramble(this.options.size, moves);\n this.physics.resetFlywheel();\n this.physics.setSpinningUp(true);\n this.scrambleRemaining = turns.length;\n this.emit('scrambleStart');\n return new Promise<void>((resolve) => {\n this.scrambleResolve = resolve;\n for (const turn of turns) {\n this.queue.push({ turn, mode: 'flywheel', duration: 0, resolve: () => {} });\n }\n });\n }\n\n /** Apply moves in standard notation, e.g. `cube.move(\"R U R' U'\")`. */\n move(notation: string, duration = this.options.moveDuration): Promise<void> {\n const turns = parseNotation(notation, this.options.size);\n return Promise.all(turns.map((t) => this.turn(t, duration))).then(() => undefined);\n }\n\n /** Apply a single turn. `duration` 0 applies it instantly. */\n turn(turn: Turn, duration = this.options.moveDuration): Promise<void> {\n if (duration <= 0 && !this.active) {\n this.applyInstant(turn);\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this.queue.push({ turn, mode: 'timed', duration, resolve });\n });\n }\n\n /** Put every cubie back to the solved state immediately. */\n reset(): void {\n this.cancelQueue();\n this.abortActive();\n for (const c of this.cubies) {\n c.grid.copy(c.home);\n c.object.position.copy(c.home);\n c.object.quaternion.identity();\n }\n this.updateSolved(true);\n }\n\n /** Programmatic hover (the same thing the mouse does). */\n setHover(hovering: boolean): void {\n if (this.hovering === hovering) return;\n this.hovering = hovering;\n this.applyIdleState();\n this.emit('hoverChange', hovering);\n }\n\n /** Turn the idle float/spin on or off regardless of hover. */\n setIdle(enabled: boolean): void {\n this.idleEnabled = enabled;\n this.applyIdleState();\n }\n\n /** Reset the cube's orientation (after orbiting / idle spinning). */\n resetOrientation(): void {\n this.cubeGroup.quaternion.identity();\n }\n\n /**\n * Update options at runtime. Physics, interaction, background, exposure, shadow opacity and\n * camera update in place; size/colours/sticker/geometry changes rebuild the cube (and reset it).\n */\n setOptions(partial: Partial<RubiksCubeOptions>): void {\n const prev = this.options;\n const p = compact(partial);\n this.options = resolveOptions({ ...prev, ...p,\n colors: { ...prev.colors, ...compact(p.colors) },\n physics: { ...prev.physics, ...compact(p.physics) },\n interaction: { ...prev.interaction, ...compact(p.interaction) },\n camera: { ...prev.camera, ...compact(p.camera) },\n sticker: { ...prev.sticker, ...compact(p.sticker) },\n });\n const o = this.options;\n this.physics.setOptions(o.physics);\n this.renderer.toneMappingExposure = o.exposure;\n this.applyBackground();\n this.camera.fov = o.camera.fov;\n this.camera.updateProjectionMatrix();\n this.placeCamera();\n if (this.shadowPlane) this.shadowPlane.material.opacity = o.shadowOpacity;\n if (o.shadow !== prev.shadow) this.buildShadow();\n\n const structural =\n o.size !== prev.size ||\n o.cubieGap !== prev.cubieGap ||\n o.cubieRounding !== prev.cubieRounding ||\n JSON.stringify(o.colors) !== JSON.stringify(prev.colors) ||\n JSON.stringify(o.sticker) !== JSON.stringify(prev.sticker);\n if (structural) {\n this.buildCube();\n this.buildShadow();\n }\n this.applyIdleState();\n }\n\n /** Tear everything down and remove the canvas. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n cancelAnimationFrame(this.rafId);\n window.clearTimeout(this.mountTimer);\n window.clearTimeout(this.touchLeaveTimer);\n this.cancelQueue();\n this.resizeObserver?.disconnect();\n window.removeEventListener('resize', this.resize);\n this.unbindPointerEvents();\n this.disposeCubies();\n this.shadowPlane?.geometry.dispose();\n this.shadowPlane?.material.dispose();\n this.envTexture.dispose();\n this.pmrem.dispose();\n this.physics.dispose();\n this.renderer.dispose();\n this.canvas.remove();\n this.listeners.clear();\n }\n\n // ═══════════════════════════════ construction ═══════════════════════════════\n\n private applyBackground(): void {\n const bg = (this.options.background ?? '').trim();\n const isPlainColor =\n bg && bg !== 'transparent' && bg !== 'none' &&\n (typeof CSS === 'undefined' || typeof CSS.supports !== 'function' || CSS.supports('color', bg));\n if (isPlainColor) {\n // solid colour → let WebGL clear to it (opaque)\n this.renderer.setClearColor(new THREE.Color(bg), 1);\n this.canvas.style.background = '';\n } else {\n // transparent, or a gradient/image → keep the canvas transparent and let CSS paint behind it\n this.renderer.setClearColor(0x000000, 0);\n this.canvas.style.background = bg && bg !== 'transparent' && bg !== 'none' ? bg : '';\n }\n }\n\n private placeCamera(): void {\n const { size, camera } = this.options;\n const distance = camera.distance ?? 3.5 * size;\n const pitch = THREE.MathUtils.degToRad(camera.pitch);\n const yaw = THREE.MathUtils.degToRad(camera.yaw);\n this.camera.position.set(\n distance * Math.cos(pitch) * Math.sin(yaw),\n distance * Math.sin(pitch),\n distance * Math.cos(pitch) * Math.cos(yaw),\n );\n this.camera.lookAt(0, 0, 0);\n }\n\n private buildCube(): void {\n this.abortActive();\n this.disposeCubies();\n const { cubies, resources } = buildCubies(this.options);\n this.cubies = cubies;\n this.resources = resources;\n for (const c of cubies) this.cubeGroup.add(c.object);\n const s = this.options.size;\n const sh = this.keyLight.shadow.camera;\n sh.left = sh.bottom = -s * 1.4;\n sh.right = sh.top = s * 1.4;\n sh.near = 1;\n sh.far = 40;\n sh.updateProjectionMatrix();\n this.updateSolved(true);\n }\n\n private disposeCubies(): void {\n for (const c of this.cubies) this.cubeGroup.remove(c.object);\n this.cubies = [];\n if (this.resources) {\n this.resources.geometries.forEach((g) => g.dispose());\n this.resources.materials.forEach((m) => m.dispose());\n this.resources = undefined;\n }\n }\n\n private buildShadow(): void {\n if (this.shadowPlane) {\n this.scene.remove(this.shadowPlane);\n this.shadowPlane.geometry.dispose();\n this.shadowPlane.material.dispose();\n this.shadowPlane = undefined;\n }\n if (!this.options.shadow) return;\n const s = this.options.size;\n const plane = new THREE.Mesh(\n new THREE.PlaneGeometry(s * 6, s * 6),\n new THREE.ShadowMaterial({ opacity: this.options.shadowOpacity, transparent: true }),\n );\n plane.rotation.x = -Math.PI / 2;\n plane.position.y = -(s / 2) * 1.05 - 0.35;\n plane.receiveShadow = true;\n this.scene.add(plane);\n this.shadowPlane = plane;\n }\n\n private squareMode = false;\n\n private resize = (): void => {\n const w = this.container.clientWidth || 300;\n // a container with no height of its own gets a square canvas\n if (!this.container.clientHeight) this.squareMode = true;\n const h = this.squareMode ? w : this.container.clientHeight;\n this.canvas.style.height = this.squareMode ? `${w}px` : '100%';\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, this.options.maxPixelRatio));\n this.renderer.setSize(w, h, false);\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n };\n\n // ═══════════════════════════════ frame loop ═══════════════════════════════\n\n private tick = (): void => {\n if (this.disposed) return;\n this.rafId = requestAnimationFrame(this.tick);\n this.timer.update();\n const dt = Math.min(this.timer.getDelta(), 0.1);\n this.elapsed += dt;\n\n this.physics.step(dt);\n this.updateTurn(dt);\n this.updateIdle(dt);\n this.cubeGroup.position.y = this.physics.floatY;\n this.renderer.render(this.scene, this.camera);\n };\n\n private updateIdle(dt: number): void {\n if (this.hovering || !this.idleEnabled) return;\n const p = this.options.physics;\n const spin = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC.y, p.idleSpin * dt);\n const tumble = new THREE.Quaternion().setFromAxisAngle(\n AXIS_VEC.x,\n Math.sin(this.elapsed * 0.7) * p.idleTumble * dt,\n );\n this.cubeGroup.quaternion.premultiply(tumble).premultiply(spin);\n }\n\n private applyIdleState(): void {\n const idle = !this.hovering && this.idleEnabled;\n this.physics.setBobbing(idle);\n this.physics.setFloatTarget(idle ? this.options.physics.floatLift : 0);\n }\n\n // ═══════════════════════════════ turning layers ═══════════════════════════════\n\n private cubiesInLayers(axis: Axis, layers: number[]): Cubie[] {\n return this.cubies.filter((c) => layers.some((l) => Math.abs(c.grid[axis] - l) < 1e-3));\n }\n\n private beginTurn(axis: Axis, layers: number[], mode: TurnMode, target: number, duration: number, resolve?: () => void): ActiveTurn {\n const pivot = new THREE.Group();\n this.cubeGroup.add(pivot);\n const cubies = this.cubiesInLayers(axis, layers);\n for (const c of cubies) pivot.attach(c.object);\n const active: ActiveTurn = {\n axis, layers, mode, pivot, cubies, angle: 0, target,\n startedAt: this.elapsed, duration, resolve,\n };\n this.active = active;\n return active;\n }\n\n private setTurnAngle(angle: number): void {\n if (!this.active) return;\n this.active.angle = angle;\n this.active.pivot.rotation.set(0, 0, 0);\n this.active.pivot.rotation[this.active.axis] = angle;\n }\n\n /** Detach cubies from the pivot, snap them to the grid, update logical state. */\n private completeTurn(): void {\n const a = this.active;\n if (!a) return;\n const quarterTurns = Math.round(a.angle / QUARTER);\n this.setTurnAngle(quarterTurns * QUARTER);\n a.pivot.updateMatrixWorld(true);\n for (const c of a.cubies) {\n this.cubeGroup.attach(c.object);\n snapGrid(c.object.position);\n snapQuaternion(c.object.quaternion);\n c.grid.copy(c.object.position);\n }\n this.cubeGroup.remove(a.pivot);\n this.active = null;\n if (a.mode === 'snap') this.physics.endSnap();\n\n a.resolve?.();\n if (quarterTurns !== 0) {\n const turn: Turn = { axis: a.axis, layers: a.layers, quarterTurns };\n this.emit('move', turn, turnToNotation(turn, this.options.size));\n this.updateSolved();\n }\n if (a.mode === 'flywheel') {\n this.scrambleRemaining -= 1;\n if (this.scrambleRemaining <= 0) this.finishScramble();\n }\n }\n\n /** Cancel the active turn without applying it (used by reset/rebuild). */\n private abortActive(): void {\n const a = this.active;\n if (!a) return;\n this.setTurnAngle(0);\n for (const c of a.cubies) {\n this.cubeGroup.attach(c.object);\n c.object.position.copy(c.grid);\n snapQuaternion(c.object.quaternion);\n }\n this.cubeGroup.remove(a.pivot);\n this.active = null;\n this.physics.endSnap();\n a.resolve?.();\n if (a.mode === 'flywheel' && this.scrambleRemaining > 0) this.finishScramble();\n if (this.drag?.kind === 'layer') this.drag = null;\n }\n\n private applyInstant(turn: Turn): void {\n this.beginTurn(turn.axis, turn.layers, 'timed', turn.quarterTurns * QUARTER, 0);\n this.setTurnAngle(turn.quarterTurns * QUARTER);\n this.completeTurn();\n }\n\n private cancelQueue(): void {\n const pending = this.queue;\n this.queue = [];\n pending.forEach((q) => q.resolve());\n if (this.scrambleRemaining > 0) {\n // let an in-flight scramble move land, then fire scrambleEnd; otherwise end now\n if (this.active?.mode === 'flywheel') this.scrambleRemaining = 1;\n else this.finishScramble();\n }\n }\n\n private finishScramble(): void {\n this.scrambleRemaining = 0;\n this.physics.setSpinningUp(false);\n const r = this.scrambleResolve;\n this.scrambleResolve = null;\n this.emit('scrambleEnd');\n r?.();\n }\n\n private updateTurn(dt: number): void {\n // start the next queued turn\n if (!this.active && this.queue.length && this.drag?.kind !== 'layer') {\n const next = this.queue.shift()!;\n const target = next.turn.quarterTurns * QUARTER;\n this.beginTurn(next.turn.axis, next.turn.layers, next.mode, target, next.duration / 1000, next.resolve);\n }\n const a = this.active;\n if (!a) return;\n\n switch (a.mode) {\n case 'timed': {\n const t = a.duration > 0 ? Math.min(1, (this.elapsed - a.startedAt) / a.duration) : 1;\n const e = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; // easeInOutCubic\n this.setTurnAngle(a.target * e);\n if (t >= 1) this.completeTurn();\n break;\n }\n case 'flywheel': {\n // keep pushing until the remaining moves are exactly what the flywheel needs to coast down\n const remaining = this.scrambleRemaining - Math.abs(a.angle) / QUARTER;\n this.physics.setSpinningUp(remaining > this.physics.quarterTurnsToCoast + 0.6);\n const dir = Math.sign(a.target);\n const next = a.angle + dir * this.physics.scrambleSpeed * dt;\n if (Math.abs(next) >= Math.abs(a.target)) {\n this.setTurnAngle(a.target);\n this.completeTurn();\n } else {\n this.setTurnAngle(next);\n }\n break;\n }\n case 'drag':\n // angle is driven by pointer events\n break;\n case 'snap': {\n this.setTurnAngle(this.physics.snapAngle);\n if (this.physics.snapSettled) {\n this.setTurnAngle(this.physics.snapTarget);\n this.completeTurn();\n }\n break;\n }\n }\n }\n\n private updateSolved(force?: boolean): void {\n const solved = force ?? this.computeSolved();\n const changed = solved !== this.solved;\n this.solved = solved;\n if (changed || force !== undefined) this.emit('stateChange', solved);\n if (changed && solved) this.emit('solved');\n }\n\n private computeSolved(): boolean {\n const byDir = new Map<string, string>();\n const n = new THREE.Vector3();\n for (const c of this.cubies) {\n for (const s of c.stickers) {\n n.copy(s.localNormal).applyQuaternion(c.object.quaternion);\n const key = `${Math.round(n.x)},${Math.round(n.y)},${Math.round(n.z)}`;\n const seen = byDir.get(key);\n if (seen === undefined) byDir.set(key, s.face);\n else if (seen !== s.face) return false;\n }\n }\n return true;\n }\n\n // ═══════════════════════════════ pointer interaction ═══════════════════════════════\n\n private bindPointerEvents(): void {\n const c = this.canvas;\n c.addEventListener('pointerdown', this.onPointerDown);\n c.addEventListener('pointermove', this.onPointerMove);\n c.addEventListener('pointerup', this.onPointerUp);\n c.addEventListener('pointercancel', this.onPointerUp);\n c.addEventListener('pointerenter', this.onPointerEnter);\n c.addEventListener('pointerleave', this.onPointerLeave);\n c.addEventListener('contextmenu', this.preventDefault);\n }\n\n private unbindPointerEvents(): void {\n const c = this.canvas;\n c.removeEventListener('pointerdown', this.onPointerDown);\n c.removeEventListener('pointermove', this.onPointerMove);\n c.removeEventListener('pointerup', this.onPointerUp);\n c.removeEventListener('pointercancel', this.onPointerUp);\n c.removeEventListener('pointerenter', this.onPointerEnter);\n c.removeEventListener('pointerleave', this.onPointerLeave);\n c.removeEventListener('contextmenu', this.preventDefault);\n }\n\n private preventDefault = (e: Event): void => e.preventDefault();\n\n private onPointerEnter = (e: PointerEvent): void => {\n if (e.pointerType === 'touch') return;\n window.clearTimeout(this.touchLeaveTimer);\n this.setHover(true);\n };\n\n private onPointerLeave = (e: PointerEvent): void => {\n if (e.pointerType === 'touch') return;\n if (this.drag) return; // keep still while a drag continues outside the canvas\n this.setHover(false);\n };\n\n private pointerNdc(e: PointerEvent): THREE.Vector2 {\n const r = this.canvas.getBoundingClientRect();\n return new THREE.Vector2(\n ((e.clientX - r.left) / r.width) * 2 - 1,\n -((e.clientY - r.top) / r.height) * 2 + 1,\n );\n }\n\n private pickCubie(e: PointerEvent): { cubie: Cubie; normal: THREE.Vector3; point: THREE.Vector3 } | null {\n this.raycaster.setFromCamera(this.pointerNdc(e), this.camera);\n const hits = this.raycaster.intersectObjects(this.cubies.map((c) => c.body), false);\n const hit = hits[0];\n if (!hit || !hit.face) return null;\n const cubie = hit.object.userData.cubie as Cubie;\n // face normal: mesh local → cube frame (cubie group is a direct child of cubeGroup,\n // or of a pivot at identity while no turn is active)\n const normal = hit.face.normal.clone().applyQuaternion(cubie.object.quaternion);\n // round to the dominant axis (rounded corners give diagonal normals)\n const ax = Math.abs(normal.x), ay = Math.abs(normal.y), az = Math.abs(normal.z);\n if (ax >= ay && ax >= az) normal.set(Math.sign(normal.x), 0, 0);\n else if (ay >= ax && ay >= az) normal.set(0, Math.sign(normal.y), 0);\n else normal.set(0, 0, Math.sign(normal.z));\n const point = this.cubeGroup.worldToLocal(hit.point.clone());\n return { cubie, normal, point };\n }\n\n private toScreen(cubePoint: THREE.Vector3): THREE.Vector2 {\n const r = this.canvas.getBoundingClientRect();\n const v = this.cubeGroup.localToWorld(cubePoint.clone()).project(this.camera);\n return new THREE.Vector2(((v.x + 1) / 2) * r.width, ((1 - v.y) / 2) * r.height);\n }\n\n private onPointerDown = (e: PointerEvent): void => {\n const io = this.options.interaction;\n if (!io.enabled || (e.pointerType === 'mouse' && e.button !== 0)) return;\n if (e.pointerType === 'touch') {\n window.clearTimeout(this.touchLeaveTimer);\n this.setHover(true);\n }\n if (this.drag) return;\n\n const scrambling = this.state === 'scrambling';\n if (scrambling && !io.interruptScramble) return;\n\n const pick = this.pickCubie(e);\n if (pick) {\n if (this.active) {\n if (scrambling) this.cancelQueue(); // interrupt: let the current move land, drop the rest\n return; // a turn is in flight — wait for it\n }\n this.drag = {\n pointerId: e.pointerId, startX: e.clientX, startY: e.clientY,\n lastX: e.clientX, lastY: e.clientY, kind: 'undecided',\n cubie: pick.cubie, normal: pick.normal, hit: pick.point, samples: [],\n };\n } else if (io.orbit) {\n this.drag = {\n pointerId: e.pointerId, startX: e.clientX, startY: e.clientY,\n lastX: e.clientX, lastY: e.clientY, kind: 'orbit', samples: [],\n };\n } else {\n return;\n }\n this.canvas.setPointerCapture(e.pointerId);\n this.canvas.style.cursor = 'grabbing';\n e.preventDefault();\n };\n\n private onPointerMove = (e: PointerEvent): void => {\n const d = this.drag;\n if (!d || d.pointerId !== e.pointerId) {\n if (!d && e.pointerType === 'mouse') {\n this.canvas.style.cursor = this.pickCubie(e) ? 'grab' : 'default';\n }\n return;\n }\n const dx = e.clientX - d.lastX;\n const dy = e.clientY - d.lastY;\n d.lastX = e.clientX;\n d.lastY = e.clientY;\n\n if (d.kind === 'orbit') {\n const s = this.options.interaction.orbitSensitivity;\n const qy = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC.y, dx * s);\n const qx = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC.x, dy * s);\n this.cubeGroup.quaternion.premultiply(qy).premultiply(qx);\n return;\n }\n\n const totalX = e.clientX - d.startX;\n const totalY = e.clientY - d.startY;\n\n if (d.kind === 'undecided') {\n if (Math.hypot(totalX, totalY) < 6) return;\n this.decideLayerDrag(d, new THREE.Vector2(totalX, totalY));\n if ((d.kind as DragSession['kind']) !== 'layer') return;\n }\n\n // drive the layer angle from the drag distance along the chosen tangent\n const along = new THREE.Vector2(totalX, totalY).dot(d.screenDir!);\n const angle = (along / this.options.interaction.dragPixelsPerQuarterTurn) * QUARTER * d.sign!;\n this.setTurnAngle(angle);\n d.samples.push({ t: performance.now(), angle });\n if (d.samples.length > 6) d.samples.shift();\n };\n\n /** Pick the rotation axis + layer from the grabbed face and the drag direction. */\n private decideLayerDrag(d: DragSession, dragPx: THREE.Vector2): void {\n const n = d.normal!;\n const hit = d.hit!;\n const cubie = d.cubie!;\n if (this.active) return; // something started in the meantime\n\n // the two tangent directions of the grabbed face\n const tangents = (['x', 'y', 'z'] as Axis[])\n .filter((ax) => Math.abs(n[ax]) < 0.5)\n .map((ax) => AXIS_VEC[ax].clone());\n\n const origin = this.toScreen(hit);\n let best: { t: THREE.Vector3; dir: THREE.Vector2; dot: number } | null = null;\n for (const t of tangents) {\n const dir = this.toScreen(hit.clone().addScaledVector(t, 0.5)).sub(origin);\n if (dir.lengthSq() < 1e-6) continue;\n dir.normalize();\n const dot = dragPx.clone().normalize().dot(dir);\n if (!best || Math.abs(dot) > Math.abs(best.dot)) best = { t, dir, dot };\n }\n if (!best) {\n this.drag = null;\n return;\n }\n // rotation axis a = n × t moves a point on the face along +t (right-hand rule)\n const a = new THREE.Vector3().crossVectors(n, best.t);\n const axis: Axis = Math.abs(a.x) > 0.5 ? 'x' : Math.abs(a.y) > 0.5 ? 'y' : 'z';\n const sign = a[axis] > 0 ? 1 : -1;\n const layer = cubie.grid[axis];\n\n d.kind = 'layer';\n d.screenDir = best.dir;\n d.sign = sign;\n this.beginTurn(axis, [layer], 'drag', 0, 0);\n }\n\n private onPointerUp = (e: PointerEvent): void => {\n const d = this.drag;\n if (e.pointerType === 'touch') {\n window.clearTimeout(this.touchLeaveTimer);\n this.touchLeaveTimer = window.setTimeout(() => this.setHover(false), 1500);\n }\n if (!d || d.pointerId !== e.pointerId) return;\n this.drag = null;\n if (this.canvas.hasPointerCapture(e.pointerId)) this.canvas.releasePointerCapture(e.pointerId);\n this.canvas.style.cursor = 'grab';\n\n if (d.kind === 'layer' && this.active?.mode === 'drag') {\n const a = this.active;\n // angular velocity from the last few samples → lets a flick complete a turn\n let velocity = 0;\n if (d.samples.length >= 2) {\n const first = d.samples[0];\n const last = d.samples[d.samples.length - 1];\n const dtMs = last.t - first.t;\n if (dtMs > 0) velocity = ((last.angle - first.angle) / dtMs) * 1000;\n }\n const predicted = a.angle + velocity * 0.12;\n const lo = Math.floor(a.angle / QUARTER) * QUARTER;\n const hi = Math.ceil(a.angle / QUARTER) * QUARTER;\n const target = Math.max(lo, Math.min(hi, Math.round(predicted / QUARTER) * QUARTER));\n a.mode = 'snap';\n a.target = target;\n this.physics.beginSnap(a.angle, velocity, target);\n }\n\n // mouse released outside the canvas → we missed the leave event\n if (e.pointerType === 'mouse') {\n const r = this.canvas.getBoundingClientRect();\n const inside = e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom;\n if (!inside) this.setHover(false);\n }\n };\n}\n","import { RubiksCubeEngine, type CubeEvent, type CubeState } from './RubiksCubeEngine';\nimport type { RubiksCubeOptions, Turn } from './types';\n\n/** The small, stable surface most integrations need. The full engine is on `.engine`. */\nexport interface RubiksCubeHandle {\n engine: RubiksCubeEngine;\n scramble(moves?: number): Promise<void>;\n move(notation: string, duration?: number): Promise<void>;\n turn(turn: Turn, duration?: number): Promise<void>;\n reset(): void;\n resetOrientation(): void;\n isSolved(): boolean;\n readonly state: CubeState;\n setHover(hovering: boolean): void;\n setIdle(enabled: boolean): void;\n setOptions(options: Partial<RubiksCubeOptions>): void;\n on(event: CubeEvent, listener: (...args: unknown[]) => void): () => void;\n destroy(): void;\n}\n\n/**\n * Mount a cube into any element (framework-free).\n *\n * const cube = createRubiksCube(document.getElementById('cube'), { size: 3 });\n * cube.move(\"R U R' U'\");\n * cube.destroy();\n */\nexport function createRubiksCube(\n container: HTMLElement | string,\n options: Partial<RubiksCubeOptions> = {},\n): RubiksCubeHandle {\n const el = typeof container === 'string' ? document.querySelector<HTMLElement>(container) : container;\n if (!el) throw new Error(`rubiks-cube-3d: container not found (${String(container)})`);\n const engine = new RubiksCubeEngine(el, options);\n return {\n engine,\n scramble: (m) => engine.scramble(m),\n move: (n, d) => engine.move(n, d),\n turn: (t, d) => engine.turn(t, d),\n reset: () => engine.reset(),\n resetOrientation: () => engine.resetOrientation(),\n isSolved: () => engine.isSolved(),\n get state() {\n return engine.state;\n },\n setHover: (h) => engine.setHover(h),\n setIdle: (i) => engine.setIdle(i),\n setOptions: (o) => engine.setOptions(o),\n on: (e, l) => engine.on(e, l),\n destroy: () => engine.dispose(),\n };\n}\n"],"mappings":"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"}
|