fix(highways): address review — bg-key alias, drum cam guard, resolver tests

Three review findings on the per-panel camera work:

- highway_3d: _bgPanelKey() resolved splitscreen via window.feedBackSplitscreen
  only, while _freeCamFor() uses the feedBackSplitscreen||slopsmithSplitscreen
  alias it claims to "mirror". If the rename lands, per-panel background settings
  would silently stop being per-panel while the camera stayed per-panel. Resolve
  the alias the same way in _bgPanelKey.
- drum_highway_3d: applyCamera()'s "before first positionCamera()" guard tested
  `_camBaseH == null`, but _camBaseH/_camBaseD were initialized to 0, so the guard
  never fired (and could apply a base-0 pose for a frame). Initialize to null.
- keys + drum: the PR claimed the Camera Director resolver was unit-checked, but
  nothing exercised it. Extract the resolver into pure, exported helpers
  (_resolveFreeCam + _ssApi), delegate the per-instance _freeCamFor to them, and
  add tests/camera_bridge.test.js covering per-panel select, global fallback,
  null-when-absent, throw-safety, and the slopsmith-alias resolution. Drum 15→21,
  keys 50→56, all pass; behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Anderson <topkoa@gmail.com>
This commit is contained in:
Kris Anderson
2026-07-09 16:15:29 -04:00
co-authored by Claude Opus 4.8
parent ff8a638d28
commit 0d4d8229c7
5 changed files with 200 additions and 34 deletions
+30 -16
View File
@@ -1600,6 +1600,29 @@
_aiRegisteredCount = 0;
}
/* ======================================================================
* Camera Director bridge resolver (pure — exported via createFactory.__test)
* ====================================================================== */
// Resolve the active splitscreen API, defensive on the global-name rename in
// flight (feedBackSplitscreen is canonical; slopsmithSplitscreen is the legacy
// alias). Returns null when splitscreen isn't present.
function _ssApi() { return window.feedBackSplitscreen || window.slopsmithSplitscreen || null; }
// Given the splitscreen API, THIS window's per-panel camera map, and the
// global camera, return this panel's camera under splitscreen, else the
// global, else null (Camera Director absent → 100% stock framing). Throw-safe
// on panelIndexFor so a misbehaving splitscreen build can't break framing.
function _resolveFreeCam(canvas, ss, panelsMap, globalCam) {
if (panelsMap && ss && typeof ss.panelIndexFor === 'function') {
try {
const i = ss.panelIndexFor(canvas);
if (i != null && panelsMap[i]) return panelsMap[i];
} catch (e) { /* ignore */ }
}
return globalCam || null;
}
/* ======================================================================
* Renderer factory
* ====================================================================== */
@@ -1850,23 +1873,12 @@
return _rigOut;
}
// Camera Director bridge resolver. Prefers THIS panel's per-panel camera
// under splitscreen (window.__h3dCamCtlPanels[panelIndex]) and falls back
// to the single global (window.__h3dCamCtl); null when Camera Director is
// absent → 100% stock framing. Defensive on the splitscreen global name
// (rename in flight: feedBackSplitscreen vs slopsmithSplitscreen).
// Camera Director bridge for THIS panel — delegates to the pure, unit-
// tested _resolveFreeCam / _ssApi (see the resolver block above the
// factory). Reads the live globals: per-panel map __h3dCamCtlPanels →
// this panel's camera, else the global __h3dCamCtl, else null (stock).
function _freeCamFor(canvas) {
const map = window.__h3dCamCtlPanels;
if (map) {
const ss = window.feedBackSplitscreen || window.slopsmithSplitscreen;
if (ss && typeof ss.panelIndexFor === 'function') {
try {
const i = ss.panelIndexFor(canvas);
if (i != null && map[i]) return map[i];
} catch (e) { /* ignore */ }
}
}
return window.__h3dCamCtl || null;
return _resolveFreeCam(canvas, _ssApi(), window.__h3dCamCtlPanels, window.__h3dCamCtl);
}
// Per-key approach glow: a key lights in its pitch-class color ONLY while a
// note is heading for it, ramping up the closer that note gets to the hit-line.
@@ -4005,6 +4017,8 @@
};
// Pure data-layer + scoring hooks for headless tests.
window.slopsmithViz_keys_highway_3d.__test = {
_resolveFreeCam,
_ssApi,
beatDurSec,
flattenNotation,
keyRange,