Make the mouth lead legible and assertable
"It feels like it does nothing" is indistinguishable from "it does nothing", and at 24fps a lead of 1 is 42ms - small enough to reasonably doubt. So the shift is now provable and visible rather than taken on trust: - shiftIndex is a pure exported function with assertions covering identity, both directions, and clamping at each end - the frame label always shows the mouth frame, not only when shifted, so the number can be watched diverging from f; non-zero leads also report in ms - the slider readout carries an explicit + sign - the stabilised pane draws the unshifted contour as a dark-green ghost when a lead is set, so the offset is something you can see Also: leadIndex called opts() on every invocation - ~14 DOM reads, once per strip thumbnail, so ~1000 per redraw at 74 frames. It reads a cached scalar now. The "different pose" assertion checks the whole track rather than one pair: synthetic poses hold for nine-frame beats, so a single pair can legitimately be identical while the shift works correctly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f9b8ec8617
commit
dc0a3f34e6
4 changed files with 56 additions and 10 deletions
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import { LIPS_OUTER, LIPS_INNER, FACE_OVAL, RIGID, subsampleSlots, subsampleRing } from './landmarks.js';
|
||||
import { fitSimilarity, applySim, procrustesMean, smoothTransforms } from './mathutil.js';
|
||||
import { stabilize, toRasterRing, selectKeys, activeKey } from './pipeline.js';
|
||||
import { stabilize, toRasterRing, selectKeys, activeKey, shiftIndex } from './pipeline.js';
|
||||
import { IndexedRaster, hexToRgb } from './raster.js';
|
||||
import { writeTake } from './take.js';
|
||||
import { otsuForTest, scaleRing } from './interior.js';
|
||||
|
|
@ -206,6 +206,31 @@ export function run() {
|
|||
ok('activeKey holds between keys',
|
||||
activeKey(sel.keys, sel.keys[1].f - 1).f === sel.keys[0].f);
|
||||
|
||||
// mouth lead: a shift that "feels like it does nothing" is indistinguishable
|
||||
// from one that does nothing, so assert the arithmetic directly.
|
||||
ok('lead 0 is identity', [0, 5, 71].every((f) => shiftIndex(f, 0, 72) === f));
|
||||
ok('positive lead moves the source frame forward', shiftIndex(10, 2, 72) === 12);
|
||||
ok('negative lead moves it back', shiftIndex(10, -3, 72) === 7);
|
||||
ok('lead clamps at the start', shiftIndex(1, -6, 72) === 0);
|
||||
ok('lead clamps at the end', shiftIndex(70, 6, 72) === 71);
|
||||
{
|
||||
// ...and that it selects different POSES, not merely different indices.
|
||||
// Checked across the whole track rather than at one pair: synthetic poses
|
||||
// hold for nine-frame beats, so any single pair can legitimately be
|
||||
// identical while the shift works perfectly.
|
||||
const N = shapes.length;
|
||||
let moved = 0, total = 0;
|
||||
for (let f = 0; f < N; f++) {
|
||||
const a = shapes[shiftIndex(f, 0, N)], b = shapes[shiftIndex(f, 3, N)];
|
||||
let d = 0;
|
||||
for (let i = 0; i < a.length; i++) d += Math.hypot(a[i].x - b[i].x, a[i].y - b[i].y);
|
||||
total++;
|
||||
if (d / a.length > 0.5) moved++;
|
||||
}
|
||||
ok('a lead of 3 changes the pose on a good share of frames', moved / total > 0.2,
|
||||
`${moved}/${total} frames differ`);
|
||||
}
|
||||
|
||||
// rasteriser: indexed, hard-edged, no blending
|
||||
const r = new IndexedRaster(64, 48);
|
||||
r.clear(0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue