mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 03:41:40 +00:00
fix(highway_3d): dolly back so the fret-number row can't clip off the bottom (#633)
The heat-coloured fret-number row is drawn as a band BELOW the board (sY(lowest) - S_GAP*1.4), but camUpdate's self-correcting framing only anchors the board CENTRE to the lower third of the screen and reserves no headroom for that row. So a tight zoom on a centred active span (worst mid-neck; fine at either end of the neck) pushes the numbers past the bottom edge -- which is why testers saw it "only when centered" and "not every song." Tilt can't fix it (it would only trade a bottom clip for a top clip); the vertical-extent problem at tight zoom needs camera distance. Add a fret-row fit guard: project the row band with the final camera and, when it falls below FRET_ROW_FIT_NDC_MIN, raise a capped, hysteretic _fretRowFitBoost applied to the curDist lerp target (the span-driven tgtDist still owns zooming IN). The boost rises promptly (proportional to the deficit), relaxes lazily past a deadband, and is capped at FRET_ROW_FIT_BOOST_MAX (+60%) so the zoom can't pop or hunt. It cooperates with the tilt loop (pull-back shrinks the scene, tilt keeps the centre anchored) and yields entirely to the Camera Director free-cam. Surgical: passages where the row is already visible never trigger it, so framing is unchanged everywhere it already worked. plugin.json 3.30.0 -> 3.30.2 (screen.js cache-buster; 3.30.1 is taken by the FPS-counter PR). Tests: tests/js/highway_3d_camera_framing.test.js (guard constants, the boosted curDist lerp, the projected-row hysteresis, free-cam yield). Fixes #632 Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
199550e5fb
commit
3120ae3e71
@@ -130,6 +130,59 @@ test('measure-start cache is invalidated on song change', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// ── Fret-row fit guard ──────────────────────────────────────────────────────
|
||||
// Keeps the heat-coloured fret-number row from clipping off the bottom edge
|
||||
// when a tight, centred zoom (worst mid-neck) drops it below the lower-third
|
||||
// framing. camUpdate dollies the camera back via a capped, hysteretic boost.
|
||||
|
||||
test('fret-row fit guard constants are defined', () => {
|
||||
for (const name of [
|
||||
'FRET_ROW_FIT_NDC_MIN', 'FRET_ROW_FIT_DEADBAND', 'FRET_ROW_FIT_BOOST_MAX',
|
||||
]) {
|
||||
assert.match(src, new RegExp('const\\s+' + name + '\\s*='),
|
||||
`${name} must be declared as a fit-guard constant`);
|
||||
}
|
||||
});
|
||||
|
||||
test('the curDist lerp target applies the fit-guard dolly boost', () => {
|
||||
// The span-driven tgtDist still owns zooming in; the boost only pulls back.
|
||||
assert.match(
|
||||
src,
|
||||
/curDist\s*\+=\s*\(\s*tgtDist\s*\*\s*_fretRowFitBoost\s*-\s*curDist\s*\)\s*\*\s*lerp/,
|
||||
'curDist must lerp toward tgtDist * _fretRowFitBoost',
|
||||
);
|
||||
});
|
||||
|
||||
test('the guard projects the fret-row band and adjusts the boost with hysteresis', () => {
|
||||
// Row band Y mirrors the render position (sY(lowest) - S_GAP * 1.4).
|
||||
assert.match(
|
||||
src,
|
||||
/Math\.min\(\s*sY\(0\)\s*,\s*sY\(nStr\s*-\s*1\)\s*\)\s*-\s*S_GAP\s*\*\s*1\.4/,
|
||||
'the guard must probe the same row band the fret-number row is drawn at',
|
||||
);
|
||||
// Prompt pull-back when below the min, capped at BOOST_MAX.
|
||||
assert.match(
|
||||
src,
|
||||
/_rowNdcY\s*<\s*FRET_ROW_FIT_NDC_MIN[\s\S]*?Math\.min\(\s*FRET_ROW_FIT_BOOST_MAX/,
|
||||
'below the min NDC the boost rises, capped at FRET_ROW_FIT_BOOST_MAX',
|
||||
);
|
||||
// Lazy relax only once past the deadband, floored at 1.
|
||||
assert.match(
|
||||
src,
|
||||
/_rowNdcY\s*>\s*FRET_ROW_FIT_NDC_MIN\s*\+\s*FRET_ROW_FIT_DEADBAND[\s\S]*?Math\.max\(\s*1\s*,\s*_fretRowFitBoost/,
|
||||
'past the deadband the boost relaxes back toward 1',
|
||||
);
|
||||
});
|
||||
|
||||
test('the fit guard yields to the free-cam (Camera Director)', () => {
|
||||
// When the free-cam owns the view the auto dolly must reset to 1, not fight it.
|
||||
assert.match(
|
||||
src,
|
||||
/if\s*\(\s*_freeCam\s*&&\s*_freeCam\.enabled\s*\)\s*\{\s*if\s*\(\s*_fretRowFitBoost\s*!==\s*1\s*\)\s*_fretRowFitBoost\s*=\s*1/,
|
||||
'with the free-cam enabled the guard must drop any auto dolly back to 1',
|
||||
);
|
||||
});
|
||||
|
||||
// ── Debug hook stayed removed ───────────────────────────────────────────────
|
||||
|
||||
test('temporary camera debug hook is not present', () => {
|
||||
|
||||
Reference in New Issue
Block a user