mirror of
https://github.com/tiennm99/rubik.git
synced 2026-09-20 04:23:48 +00:00
refactor: simplifier pass — collapse duplicate code paths
- animate-move: factored shared promise/Tween body into tweenPivot; animateMove builds the pivot from spec, snapAndAnimate accepts a caller-built pivot. 96 → 80 LOC, no behavior change. - pointer-gesture: merged abortGesture / finishGesture / dispose busy release into a single idempotent endGesture; ownedBusy already encoded the per-gesture semantics. 205 → 190 LOC. - App: dropped the redundant `solving` state; ControlsPanel derives it as `busy && !solveActive`. runSolveStep collapsed to a one-line delegate since the busy gate now lives in CubeView. - CubeView: extracted cubejs lazy-load + parse into computeSolvePlan and dropped the dead bracket-undo guard. 41 tests still green; build clean. No deps, features, or behavior changes. Refs: plans/reports/code-simplifier-260427-1848-rubik-pass.md
This commit is contained in:
1 parent
338b65fa54
commit
d19a9246d5
6 files changed
+133
-120
No files matched your search
@@ -76,7 +76,7 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
if (isBusy && isBusy()) {
|
||||
// Keyboard animation started during PROBING. We never owned busy,
|
||||
// so abort this gesture without clearing it.
|
||||
abortGesture();
|
||||
endGesture();
|
||||
return;
|
||||
}
|
||||
const hitWorldPos = hitMesh.getWorldPosition(new Vector3());
|
||||
@@ -124,7 +124,7 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
function onPointerUp(e) {
|
||||
if (e.pointerId !== pointerId) return;
|
||||
if (state !== STATE_DRAGGING) {
|
||||
abortGesture();
|
||||
endGesture();
|
||||
return;
|
||||
}
|
||||
const turns = Math.round(curAngle / HALF_PI);
|
||||
@@ -136,7 +136,7 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
parentGroup, pivot, meshes, cubies,
|
||||
spec: dummy,
|
||||
fromAngle: curAngle, toAngle: 0, durationMs: 100
|
||||
}).then(finishGesture);
|
||||
}).then(() => endGesture());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,22 +148,17 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
fromAngle: curAngle, toAngle: targetAngle, durationMs: 120
|
||||
}).then(() => {
|
||||
onMoveCommitted?.(specToName(spec));
|
||||
finishGesture();
|
||||
endGesture();
|
||||
});
|
||||
}
|
||||
|
||||
// Tear down a gesture that NEVER reached DRAGGING — leaves busy alone.
|
||||
function abortGesture() {
|
||||
releasePointer();
|
||||
controls.enabled = true;
|
||||
state = STATE_IDLE;
|
||||
pivot = null;
|
||||
hitMesh = null;
|
||||
}
|
||||
|
||||
// Tear down a gesture that DID reach DRAGGING — releases busy.
|
||||
function finishGesture() {
|
||||
releasePointer();
|
||||
// Single teardown path. Releases pointer capture + restores OrbitControls,
|
||||
// and releases the busy gate iff *this* gesture had taken it.
|
||||
function endGesture() {
|
||||
if (pointerId !== null) {
|
||||
try { canvas.releasePointerCapture(pointerId); } catch {}
|
||||
pointerId = null;
|
||||
}
|
||||
controls.enabled = true;
|
||||
state = STATE_IDLE;
|
||||
pivot = null;
|
||||
@@ -174,13 +169,6 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
}
|
||||
}
|
||||
|
||||
function releasePointer() {
|
||||
if (pointerId !== null) {
|
||||
try { canvas.releasePointerCapture(pointerId); } catch {}
|
||||
pointerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function clamp(v, lo, hi) { return Math.min(hi, Math.max(lo, v)); }
|
||||
|
||||
canvas.addEventListener('pointerdown', onPointerDown);
|
||||
@@ -196,10 +184,7 @@ export function setupPointerGesture({ canvas, camera, controls, parentGroup, mes
|
||||
canvas.removeEventListener('pointercancel', onPointerUp);
|
||||
// If unmount happens mid-drag, release busy so the parent isn't
|
||||
// permanently locked. Safe even if !ownedBusy (no-op).
|
||||
if (ownedBusy) {
|
||||
setBusy?.(false);
|
||||
ownedBusy = false;
|
||||
}
|
||||
endGesture();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
// Animates a layer rotation: temporarily reparent moving meshes into a
|
||||
// THREE.Group, tween group.rotation around the move axis, then bake world
|
||||
// transforms back into the parent group and update the model.
|
||||
//
|
||||
// `applyMoveFn` is called at the end so the cubie model reflects the new state.
|
||||
|
||||
import { Group, Quaternion, Vector3 } from 'three';
|
||||
import { Group } from 'three';
|
||||
import { Tween, Easing, Group as TweenGroup } from '@tweenjs/tween.js';
|
||||
import { applyMove, HALF_PI } from '../core/apply-move.js';
|
||||
import { syncMeshes } from './cubie-meshes.js';
|
||||
@@ -26,53 +24,12 @@ export function clearTweens() {
|
||||
TWEENS.removeAll();
|
||||
}
|
||||
|
||||
export function animateMove({ parentGroup, meshes, cubies, spec, durationMs = 200 }) {
|
||||
return new Promise((resolve) => {
|
||||
const idx = AXIS_INDEX[spec.axis];
|
||||
const moving = meshes.filter((m) => {
|
||||
if (spec.layer === null) return true;
|
||||
return Math.round(m.userData.cubie.position[idx]) === spec.layer;
|
||||
});
|
||||
|
||||
const pivot = new Group();
|
||||
parentGroup.add(pivot);
|
||||
for (const mesh of moving) {
|
||||
pivot.attach(mesh);
|
||||
}
|
||||
|
||||
const targetAngle = spec.sign * HALF_PI * spec.count;
|
||||
const state = { a: 0 };
|
||||
|
||||
new Tween(state, TWEENS)
|
||||
.to({ a: targetAngle }, durationMs)
|
||||
.easing(Easing.Cubic.Out)
|
||||
.onUpdate(() => {
|
||||
pivot.rotation[spec.axis] = state.a;
|
||||
})
|
||||
.onComplete(() => {
|
||||
try {
|
||||
pivot.rotation[spec.axis] = targetAngle;
|
||||
pivot.updateMatrixWorld(true);
|
||||
for (const mesh of moving) {
|
||||
parentGroup.attach(mesh);
|
||||
}
|
||||
parentGroup.remove(pivot);
|
||||
applyMove(cubies, spec);
|
||||
syncMeshes(meshes);
|
||||
} finally {
|
||||
// Resolve even on failure so the awaiter never deadlocks
|
||||
// with busy=true.
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
});
|
||||
}
|
||||
|
||||
export function snapAndAnimate({ parentGroup, pivot, meshes, cubies, spec, fromAngle, toAngle, durationMs = 120 }) {
|
||||
// Core tween. Caller supplies an already-populated pivot Group plus
|
||||
// from/to angles; we tween, then bake meshes back to parentGroup and
|
||||
// optionally apply the move spec to the cubie model.
|
||||
function tweenPivot({ parentGroup, pivot, meshes, cubies, spec, fromAngle, toAngle, durationMs, applyToModel }) {
|
||||
return new Promise((resolve) => {
|
||||
const state = { a: fromAngle };
|
||||
const wantApply = Math.abs(toAngle) > 1e-3;
|
||||
new Tween(state, TWEENS)
|
||||
.to({ a: toAngle }, durationMs)
|
||||
.easing(Easing.Cubic.Out)
|
||||
@@ -85,12 +42,39 @@ export function snapAndAnimate({ parentGroup, pivot, meshes, cubies, spec, fromA
|
||||
parentGroup.attach(mesh);
|
||||
}
|
||||
parentGroup.remove(pivot);
|
||||
if (wantApply) applyMove(cubies, spec);
|
||||
if (applyToModel) applyMove(cubies, spec);
|
||||
syncMeshes(meshes);
|
||||
} finally {
|
||||
resolve(wantApply);
|
||||
// Resolve even on failure so the awaiter never deadlocks
|
||||
// with busy=true.
|
||||
resolve(applyToModel);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
});
|
||||
}
|
||||
|
||||
export function animateMove({ parentGroup, meshes, cubies, spec, durationMs = 200 }) {
|
||||
const idx = AXIS_INDEX[spec.axis];
|
||||
const pivot = new Group();
|
||||
parentGroup.add(pivot);
|
||||
for (const mesh of meshes) {
|
||||
if (spec.layer === null || Math.round(mesh.userData.cubie.position[idx]) === spec.layer) {
|
||||
pivot.attach(mesh);
|
||||
}
|
||||
}
|
||||
const targetAngle = spec.sign * HALF_PI * spec.count;
|
||||
return tweenPivot({
|
||||
parentGroup, pivot, meshes, cubies, spec,
|
||||
fromAngle: 0, toAngle: targetAngle,
|
||||
durationMs, applyToModel: true
|
||||
});
|
||||
}
|
||||
|
||||
export function snapAndAnimate({ parentGroup, pivot, meshes, cubies, spec, fromAngle, toAngle, durationMs = 120 }) {
|
||||
return tweenPivot({
|
||||
parentGroup, pivot, meshes, cubies, spec,
|
||||
fromAngle, toAngle, durationMs,
|
||||
applyToModel: Math.abs(toAngle) > 1e-3
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user