mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-11 01:44:31 +00:00
fix(h3d-carve-14): restore dropped tw arg in tremoloOffsetWorldX call; distinguish mStr/mGlow fakes
Creed F1 (MED): slideRibbonUpdatePositions called tremoloOffsetWorldX(n, Tk) — the
move dropped the tw (ribbon width) argument. Helper signature is (n, chartTime, trailW);
with trailW=undefined every ribbon vertex position was NaN for notes with n.tr truthy
(tremolo slide sustains). Restored verbatim: tremoloOffsetWorldX(n, Tk, tw).
Creed F2 (LOW): _buildDrawNote gave getMStr and getMGlow identical fakeMat objects, so
swapping the wiring left the suite green. Now each string index gets its own named object
(mStr[i]/mGlow[i]); added behavioral assertion that gem core.material === mStrMats[s].
Tests: 26/26 note-renderer pass (+2 new kills: tests 25 and 26). Suite 1379/2 (same
2 pre-existing failures as 59bccef baseline).
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
59bccefe00
commit
b02c760aec
@@ -112,7 +112,7 @@ export function createNoteRenderer({
|
||||
const zk = dZ(Tk - now);
|
||||
const xc = strandBaseX
|
||||
+ dirMul * slideOffsetWorldX(n, Tk, slideSt)
|
||||
+ tremoloOffsetWorldX(n, Tk);
|
||||
+ tremoloOffsetWorldX(n, Tk, tw); // h3d-carve-14 creed-fix: restore dropped tw arg
|
||||
const yc = y + techniqueYOffsetWorld(n, Tk);
|
||||
pa[v++] = xc - tw * 0.5; pa[v++] = yc - th * 0.5; pa[v++] = zk;
|
||||
pa[v++] = xc + tw * 0.5; pa[v++] = yc - th * 0.5; pa[v++] = zk;
|
||||
|
||||
@@ -264,6 +264,9 @@ function _buildDrawNote(overrides) {
|
||||
const pNotePool = { get: () => { pNoteGetCount++; return fakeMesh; }, release: () => {} };
|
||||
const noopPool = { get: () => fakeMesh, release: () => {} };
|
||||
const A6 = (v) => [v, v, v, v, v, v];
|
||||
// Creed F2 fix: distinguishable materials so getMStr/getMGlow swap is visible.
|
||||
const mStrMats = [0,1,2,3,4,5].map(i => ({ opacity: 1, depthTest: true, name: `mStr[${i}]` }));
|
||||
const mGlowMats = [0,1,2,3,4,5].map(i => ({ opacity: 1, depthTest: true, name: `mGlow[${i}]` }));
|
||||
|
||||
const di = Object.assign({
|
||||
// Constants
|
||||
@@ -364,8 +367,8 @@ function _buildDrawNote(overrides) {
|
||||
getPTeachMarkLbl: () => noopPool,
|
||||
getPTechPlane: () => noopPool,
|
||||
// Material getters
|
||||
getMStr: () => A6(fakeMat),
|
||||
getMGlow: () => A6(fakeMat),
|
||||
getMStr: () => mStrMats,
|
||||
getMGlow: () => mGlowMats,
|
||||
getMSus: () => fakeMat,
|
||||
getMSusOutline: () => fakeMat,
|
||||
getMHitBright: () => A6(fakeMat),
|
||||
@@ -390,7 +393,7 @@ function _buildDrawNote(overrides) {
|
||||
}, overrides || {});
|
||||
|
||||
const { drawNote } = _createNoteRendererFn(di);
|
||||
return { drawNote, getPNoteGetCount: () => pNoteGetCount };
|
||||
return { drawNote, getPNoteGetCount: () => pNoteGetCount, mStrMats, mGlowMats, getFakeMesh: () => fakeMesh };
|
||||
}
|
||||
|
||||
test('drawNote: past-linger note exits before pNote.get() (early-exit kill)', () => {
|
||||
@@ -413,3 +416,57 @@ test('drawNote: in-window note reaches pNote.get() × 2 (gem-path kill)', () =>
|
||||
assert.ok(getPNoteGetCount() >= 2,
|
||||
`pNote.get() must be called at least twice (outline + core) for an in-window note (got ${getPNoteGetCount()})`);
|
||||
});
|
||||
|
||||
test('drawNote: gem core.material is mStr[s], not mGlow[s] (material-identity kill)', () => {
|
||||
// Creed F2: fake materials were identical; swapping getMStr/getMGlow in the wiring
|
||||
// stayed green. Now mStrMats/mGlowMats are distinct objects.
|
||||
// Kill: swap getMStr/getMGlow in _buildDrawNote overrides → core.material === mGlowMats[0] → RED.
|
||||
const s = 0;
|
||||
const { drawNote, mStrMats, mGlowMats, getFakeMesh } = _buildDrawNote({ getNdHasProvider: () => false });
|
||||
drawNote({ s, f: 5, t: 5, sus: 0 }, /*now=*/5, 0, false, false, 0.10);
|
||||
const mesh = getFakeMesh();
|
||||
assert.strictEqual(mesh.material, mStrMats[s],
|
||||
`gem core.material must be mStr[${s}] (got: ${mesh.material && mesh.material.name})`);
|
||||
assert.notStrictEqual(mesh.material, mGlowMats[s],
|
||||
'gem core.material must NOT be mGlow (getMStr/getMGlow swap must be visible)');
|
||||
});
|
||||
|
||||
test('slideRibbonUpdatePositions: all vertex positions finite for n.tr sustain (NaN-arg kill)', () => {
|
||||
// Creed F1: tremoloOffsetWorldX(n, Tk) dropped tw → undefined*…=NaN for all ribbon vertices.
|
||||
// Fix: tremoloOffsetWorldX(n, Tk, tw). Kill: drop tw arg again → NaN in posArray → RED.
|
||||
const S = 8; // matches DI SLIDE_RIBBON_SAMPLES
|
||||
const posArray = new Float32Array((S + 1) * 4 * 3);
|
||||
posArray.fill(NaN); // pre-fill NaN: if path not taken, assertion catches it (test setup bug)
|
||||
let geoWritten = false;
|
||||
const makeRibbonMesh = () => ({
|
||||
position: { set: () => {} },
|
||||
rotation: { set: () => {}, z: 0 },
|
||||
scale: { set: () => {} },
|
||||
renderOrder: 0, visible: true, material: null,
|
||||
geometry: {
|
||||
attributes: {
|
||||
position: {
|
||||
array: posArray,
|
||||
set needsUpdate(v) { if (v) geoWritten = true; },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const ribbonPool = { get: makeRibbonMesh, release: () => {} };
|
||||
|
||||
const { drawNote } = _buildDrawNote({
|
||||
getNdHasProvider: () => false,
|
||||
getPSusRibbon: () => ribbonPool,
|
||||
getPSusRibbonOl: () => ribbonPool,
|
||||
SLIDE_RIBBON_SAMPLES: S,
|
||||
});
|
||||
|
||||
// sus=0.5 (remSus=0.5>0.01), tr=1 → ribbonSusTrail=true → slideRibbonUpdatePositions called
|
||||
drawNote({ s: 0, f: 5, t: 5, sus: 0.5, tr: 1 }, /*now=*/5, 0, false, false, 0.10);
|
||||
|
||||
assert.ok(geoWritten, 'geometry.needsUpdate must be set — ribbon path must be reached');
|
||||
for (let i = 0; i < posArray.length; i++) {
|
||||
assert.ok(Number.isFinite(posArray[i]),
|
||||
`posArray[${i}] must be finite; NaN = dropped tw arg in tremoloOffsetWorldX`);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user