[auto] highlight active step detection segments on graphs

Yellow background highlights the time range when a step is active
(from detection until reset). L/R graph highlights step-left and
step-right, F/B graph highlights step-forward and step-back.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
main
name 2026-04-11 12:52:50 -07:00
parent 26e3df6ad2
commit 9ad8dea4ff
2 changed files with 36 additions and 16 deletions

View File

@ -20,6 +20,7 @@ function drawGraph(
getRight: (s: StepGraphSample) => number,
threshold: number,
reset: number,
activeSignals: string[], // which step signals to highlight for this graph
) {
// Background
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
@ -39,6 +40,17 @@ function drawGraph(
const toY = (v: number) => h / 2 - (v / maxAbs) * (h / 2 - 4)
const toX = (i: number) => (i / (samples.length - 1)) * w
// Highlight active step segments
for (let i = 0; i < samples.length; i++) {
const s = samples[i]
if (s.activeStep && activeSignals.includes(s.activeStep)) {
const x0 = toX(i)
const x1 = i < samples.length - 1 ? toX(i + 1) : x0 + 2
ctx.fillStyle = 'rgba(255, 255, 0, 0.15)'
ctx.fillRect(x0, 0, x1 - x0, h)
}
}
// Zero line
ctx.strokeStyle = '#555'
ctx.lineWidth = 1
@ -124,10 +136,12 @@ export function StepGraphs({ samples, thresholdX, thresholdZ, reset, width = 280
const fbCtx = fbCanvas.getContext('2d')!
drawGraph(lrCtx, samples, width, height, 'LEFT / RIGHT (X)',
(s) => s.leftX, (s) => s.rightX, thresholdX, reset)
(s) => s.leftX, (s) => s.rightX, thresholdX, reset,
['step-left', 'step-right'])
drawGraph(fbCtx, samples, width, height, 'FORWARD / BACK (Z)',
(s) => s.leftZ, (s) => s.rightZ, thresholdZ, reset)
(s) => s.leftZ, (s) => s.rightZ, thresholdZ, reset,
['step-forward', 'step-back'])
}, [samples, thresholdX, thresholdZ, reset, width, height])
return (

View File

@ -37,6 +37,7 @@ export interface StepGraphSample {
rightX: number // right heel X displacement from baseline
leftZ: number // left heel Z displacement from baseline
rightZ: number // right heel Z displacement from baseline
activeStep: GestureSignal // which step is active this frame (null if none)
}
export interface GestureState {
@ -285,23 +286,9 @@ export function detectGesture(
}
if (!leftAirborne || !rightAirborne) state.wasAirborne = false
// Collect step graph samples
if (state.heelBaseL && state.heelBaseR && worldLandmarks?.length >= 33) {
const lh = worldLandmarks[29], rh = worldLandmarks[30]
state.stepGraph.push({
time: now,
leftX: lh.x - state.heelBaseL.x,
rightX: rh.x - state.heelBaseR.x,
leftZ: lh.z - state.heelBaseL.z,
rightZ: rh.z - state.heelBaseR.z,
})
if (state.stepGraph.length > GRAPH_HISTORY) state.stepGraph.shift()
}
// Step detection (worldLandmarks heel displacement from baseline)
if (state.heelBaseL && state.heelBaseR && worldLandmarks?.length >= 33) {
if (state.activeStep) {
// Check if feet returned to center
if (stepsReset(worldLandmarks, state.heelBaseL, state.heelBaseR, stepCfg)) {
state.activeStep = null
}
@ -309,9 +296,28 @@ export function detectGesture(
const step = detectStep(worldLandmarks, state.heelBaseL, state.heelBaseR, stepCfg)
if (step) {
state.activeStep = step
// Collect sample before emitting (so this frame is captured)
const lh = worldLandmarks[29], rh = worldLandmarks[30]
state.stepGraph.push({
time: now,
leftX: lh.x - state.heelBaseL.x, rightX: rh.x - state.heelBaseR.x,
leftZ: lh.z - state.heelBaseL.z, rightZ: rh.z - state.heelBaseR.z,
activeStep: state.activeStep,
})
if (state.stepGraph.length > GRAPH_HISTORY) state.stepGraph.shift()
return emit(step)
}
}
// Collect step graph sample (after detection so activeStep is current)
const lh = worldLandmarks[29], rh = worldLandmarks[30]
state.stepGraph.push({
time: now,
leftX: lh.x - state.heelBaseL.x, rightX: rh.x - state.heelBaseR.x,
leftZ: lh.z - state.heelBaseL.z, rightZ: rh.z - state.heelBaseR.z,
activeStep: state.activeStep,
})
if (state.stepGraph.length > GRAPH_HISTORY) state.stepGraph.shift()
}
// Palms together