/ simulated_instruments / woodblock
Sign in
simulated_instruments / woodblock
woodblock / webapp / src / components / plots / ContactForcePlot.tsx
73 lines · 2.3 KBBlameHistoryRaw
1// Contact-force pulse f_c(t). Its width sets brightness: harder mallets give
2// a shorter pulse and therefore more high-frequency drive.
4import { useCanvas, drawFrame, PLOT_COLORS } from './canvasUtils';
6interface Props {
7 force: Float32Array;
8 forceFsHz: number;
9 peakForceN: number;
10 contactMs: number;
13export default function ContactForcePlot({ force, forceFsHz, peakForceN, contactMs }: Props) {
14 const ref = useCanvas(
15 (ctx, w, h) => {
16 drawFrame(ctx, w, h);
17 const padL = 44;
18 const padB = 16;
19 const pw = w - padL - 8;
20 const ph = h - padB - 10;
22 // window: from 0 to a bit past the end of contact, at least 1 ms
23 let lastIdx = 0;
24 for (let i = 0; i < force.length; i++) if (force[i] > 0) lastIdx = i;
25 const winMs = Math.max(1.0, ((lastIdx / forceFsHz) * 1e3) * 1.4);
26 const nWin = Math.min(force.length, Math.round((winMs / 1e3) * forceFsHz));
28 let peak = 0;
29 for (let i = 0; i < nWin; i++) peak = Math.max(peak, force[i]);
30 if (peak <= 0) peak = 1;
32 ctx.font = '10px system-ui';
33 ctx.fillStyle = PLOT_COLORS.axis;
34 ctx.textAlign = 'center';
35 const step = winMs > 4 ? 1.0 : winMs > 2 ? 0.5 : 0.2;
36 for (let t = 0; t <= winMs + 1e-9; t += step) {
37 const x = padL + (t / winMs) * pw;
38 ctx.strokeStyle = PLOT_COLORS.grid;
39 ctx.beginPath();
40 ctx.moveTo(x, 10);
41 ctx.lineTo(x, 10 + ph);
42 ctx.stroke();
43 ctx.fillText(`${t.toFixed(1)}ms`, x, h - 4);
44 }
45 ctx.textAlign = 'right';
46 ctx.fillText(`${peak.toFixed(0)} N`, padL - 4, 16);
47 ctx.fillText('0', padL - 4, 10 + ph);
49 ctx.strokeStyle = PLOT_COLORS.accent;
50 ctx.lineWidth = 1.5;
51 ctx.beginPath();
52 for (let px = 0; px <= pw; px++) {
53 const i = Math.min(nWin - 1, Math.round((px / pw) * (nWin - 1)));
54 const yy = 10 + ph - (force[i] / peak) * ph;
55 if (px === 0) ctx.moveTo(padL, yy);
56 else ctx.lineTo(padL + px, yy);
57 }
58 ctx.stroke();
59 ctx.lineWidth = 1;
61 ctx.fillStyle = PLOT_COLORS.axis;
62 ctx.textAlign = 'left';
63 ctx.fillText(
64 `contact ${contactMs.toFixed(3)} ms · peak ${peakForceN.toFixed(0)} N`,
65 padL + 6,
66 16,
67 );
68 },
69 [force, forceFsHz, peakForceN, contactMs],
70 170,
71 );
72 return <canvas ref={ref} />;
↑↓ move↵openescclose