mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-13 01:48:30 +00:00
h3d-carve-13: extract S-section lookahead helpers into src/camera.js
Partial cut (Option A) per contract ~/Feedback-harness/hive/plans/h3d-cut13-contract.md. What moves: - lookaheadEndTime (factory-private) — 1 beyond-subst: _measureStarts → getMeasureStarts() - lookaheadBootstrapTime (exported) - lookaheadComputeFretBounds (exported) — uses lowerBoundT from geometry.js (new import) - lookaheadTargetWorldX (exported) DI delta: +9 params to createCamera() Constants (+4): NFRETS=24, CAM_LOOKAHEAD_MEASURES=9, CAM_LOOKAHEAD_SEC=3.0, CAM_FRET_EDGE_BLEND=0.1 Getter (+1): getMeasureStarts: () => _measureStarts Fn refs (+4): validString, getChartAnchorAt, xFretMid, xFret Total createCamera() DI: 57 (was 48) Deferred to cut 15 (U-section entanglement): lookaheadSmoothCamStep, _applyNoteCamTargets — write S-section state vars Region B (per-frame cam target block, ~90 lines) — reads 8 update() locals Region C (song-change + bootstrap block, ~174 lines) — U-section init code Call sites unchanged (destructured names unchanged): lookaheadComputeFretBounds: lines 7474, 7941 lookaheadBootstrapTime: line 7939 lookaheadTargetWorldX: lines 7943, 10062 Tests: 17 new (highway_3d_camera_lookahead.test.js), 2 retargeted to cameraSrc. Suite: 1353/1355 pass (2 pre-existing failures in unrelated test files). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014uZ169yfoFYArXz962g7KW
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e230629770
commit
66aa829362
@@ -6621,102 +6621,9 @@ import { createArp } from './src/arp.js'; // h3d-carve-12
|
||||
getMAccentCore: () => mAccentCore,
|
||||
});
|
||||
|
||||
/* ── Lookahead fret bounds + smooth camera ───────────────────────── */
|
||||
// End time of the lookahead window = start of the measure that is
|
||||
// CAM_LOOKAHEAD_MEASURES measures ahead of the current one. Uses the
|
||||
// _measureStarts cache (times of beats with measure !== -1). With no
|
||||
// beats it falls back to CAM_LOOKAHEAD_SEC seconds. Past the last known
|
||||
// measure it extrapolates using the average measure duration.
|
||||
function lookaheadEndTime(now) {
|
||||
const ms = _measureStarts;
|
||||
if (!ms || ms.length === 0) return now + CAM_LOOKAHEAD_SEC;
|
||||
// Binary search: lo = first index with ms[lo] > now.
|
||||
let lo = 0, hi = ms.length;
|
||||
while (lo < hi) { const mid = (lo + hi) >> 1; if (ms[mid] <= now) lo = mid + 1; else hi = mid; }
|
||||
const curIdx = lo - 1; // current measure (-1 if before the first)
|
||||
const targetIdx = curIdx + CAM_LOOKAHEAD_MEASURES;
|
||||
if (targetIdx >= 0 && targetIdx < ms.length) return ms[targetIdx];
|
||||
// Past the last measure: extrapolate using the average measure duration.
|
||||
if (ms.length >= 2) {
|
||||
const avg = (ms[ms.length - 1] - ms[0]) / (ms.length - 1);
|
||||
if (avg > 0) return ms[ms.length - 1] + (targetIdx - (ms.length - 1)) * avg;
|
||||
}
|
||||
return now + CAM_LOOKAHEAD_SEC;
|
||||
}
|
||||
|
||||
// Earliest future chart time whose lookahead end reaches eventTime.
|
||||
// lookaheadEndTime() is monotonic but measure-stepped, so a small
|
||||
// bounded binary search works for both measure grids and the seconds
|
||||
// fallback without duplicating/inverting its edge-case logic.
|
||||
function lookaheadBootstrapTime(now, eventTime) {
|
||||
if (!(eventTime > now) || lookaheadEndTime(now) >= eventTime) return now;
|
||||
let lo = now;
|
||||
let hi = eventTime;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
const mid = (lo + hi) * 0.5;
|
||||
if (lookaheadEndTime(mid) >= eventTime) hi = mid;
|
||||
else lo = mid;
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
function lookaheadComputeFretBounds(now, anchors, notes, chords) {
|
||||
const tEnd = lookaheadEndTime(now);
|
||||
let minF = 99;
|
||||
let maxF = 0;
|
||||
let any = false;
|
||||
if (anchors && anchors.length) {
|
||||
for (let tt = now; tt <= tEnd + 1e-9; tt += 0.125) {
|
||||
const a = getChartAnchorAt(anchors, tt);
|
||||
if (!a) continue;
|
||||
let fStart = Math.round(Number(a.fret));
|
||||
if (!Number.isFinite(fStart) || fStart < 1) fStart = 1;
|
||||
let w = Number(a.width);
|
||||
if (!Number.isFinite(w)) w = 4;
|
||||
w = Math.max(1, Math.round(w));
|
||||
const fHi = Math.min(NFRETS, fStart + w - 1);
|
||||
minF = Math.min(minF, fStart);
|
||||
maxF = Math.max(maxF, fHi);
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
const consider = f => {
|
||||
if (!(f > 0)) return;
|
||||
minF = Math.min(minF, f);
|
||||
maxF = Math.max(maxF, f);
|
||||
any = true;
|
||||
};
|
||||
if (notes) {
|
||||
let i = lowerBoundT(notes, now);
|
||||
for (; i < notes.length; i++) {
|
||||
const n = notes[i];
|
||||
if (n.t > tEnd) break;
|
||||
if (!validString(n.s)) continue;
|
||||
consider(n.f);
|
||||
}
|
||||
}
|
||||
if (chords) {
|
||||
let i = lowerBoundT(chords, now);
|
||||
for (; i < chords.length; i++) {
|
||||
const ch = chords[i];
|
||||
if (ch.t > tEnd) break;
|
||||
if (!ch.notes) continue;
|
||||
for (const cn of ch.notes) {
|
||||
if (!validString(cn.s)) continue;
|
||||
consider(cn.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!any || minF > maxF) return null;
|
||||
return { minF, maxF };
|
||||
}
|
||||
|
||||
function lookaheadTargetWorldX(minF, maxF) {
|
||||
const wb = CAM_FRET_EDGE_BLEND;
|
||||
const middle = (xFretMid(minF) + xFretMid(maxF)) * 0.5;
|
||||
const weighted = 0.6 * xFret(0) + 0.4 * xFret(NFRETS);
|
||||
return middle * (1 - wb) + weighted * wb;
|
||||
}
|
||||
/* ── h3d-carve-13: S-section lookahead helpers → src/camera.js ──── */
|
||||
// lookaheadEndTime (private), lookaheadBootstrapTime, lookaheadComputeFretBounds,
|
||||
// lookaheadTargetWorldX extracted below in the createCamera() destructure.
|
||||
|
||||
function lookaheadSmoothCamStep(dtSec, tgtXWorld, tgtSpanInt) {
|
||||
const d = Math.min(0.2, Math.max(1e-4, dtSec));
|
||||
@@ -11698,7 +11605,7 @@ import { createArp } from './src/arp.js'; // h3d-carve-12
|
||||
/* ── h3d-carve-10: drawScoreFx → src/score-fx.js ────────────────── */
|
||||
|
||||
/* ── h3d-carve-9: W-section (camera lerp) → src/camera.js ─────── */
|
||||
const { effectiveVfov, camUpdate } = createCamera({
|
||||
const { effectiveVfov, camUpdate, lookaheadBootstrapTime, lookaheadComputeFretBounds, lookaheadTargetWorldX } = createCamera({
|
||||
// Constants
|
||||
BASE_VFOV, HORPLUS_START_ASPECT, HORPLUS_MIN_VFOV,
|
||||
CAM_LERP_BASE, CAM_H_BASE, CAM_DIST_BASE,
|
||||
@@ -11728,6 +11635,10 @@ import { createArp } from './src/arp.js'; // h3d-carve-12
|
||||
aspectPaneKey: _aspectPaneKey,
|
||||
resolveTuneFor: _resolveTuneFor,
|
||||
aspectRegisterPane: _aspectRegisterPane,
|
||||
// h3d-carve-13: S-section lookahead (+9 DI params)
|
||||
NFRETS, CAM_LOOKAHEAD_MEASURES, CAM_LOOKAHEAD_SEC, CAM_FRET_EDGE_BLEND,
|
||||
getMeasureStarts: () => _measureStarts,
|
||||
validString, getChartAnchorAt, xFretMid, xFret,
|
||||
});
|
||||
|
||||
/* ── Resize helper ───────────────────────────────────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user