aa03146Maraca shake simulator: MATLAB model and interactive web appmagland 1import { useEffect, useRef } from 'react';
3// Canvas hook: resizes to element width at devicePixelRatio and calls draw.
4export function useCanvas(
5 draw: (ctx: CanvasRenderingContext2D, w: number, h: number) => void,
6 deps: unknown[],
7 height: number,
8) {
9 const ref = useRef<HTMLCanvasElement | null>(null);
11 useEffect(() => {
12 const canvas = ref.current;
13 if (!canvas) return;
14 const render = () => {
15 const dpr = window.devicePixelRatio || 1;
16 const w = canvas.clientWidth;
17 canvas.width = Math.max(1, Math.round(w * dpr));
18 canvas.height = Math.max(1, Math.round(height * dpr));
19 canvas.style.height = `${height}px`;
20 const ctx = canvas.getContext('2d')!;
21 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
22 ctx.clearRect(0, 0, w, height);
23 draw(ctx, w, height);
24 };
25 render();
26 const ro = new ResizeObserver(render);
27 ro.observe(canvas);
28 return () => ro.disconnect();
29 // eslint-disable-next-line react-hooks/exhaustive-deps
30 }, deps);
32 return ref;
33}
35export const PLOT_COLORS = {
36 bg: '#181b20',
37 grid: '#2a2f38',
38 axis: '#8b93a1',
39 trace: '#4dd0e1',
40 accent: '#e8a33d',
41 good: '#6fbf73',
42 warn: '#e57373',
43};
45export function drawFrame(ctx: CanvasRenderingContext2D, w: number, h: number) {
46 ctx.fillStyle = PLOT_COLORS.bg;
47 ctx.fillRect(0, 0, w, h);
48}
50// Viridis-like colormap for the spectrogram, t in [0,1].
51export function colormap(t: number): [number, number, number] {
52 const stops: [number, number, number][] = [
53 [13, 8, 60], [84, 39, 143], [140, 41, 129], [196, 66, 90],
54 [237, 105, 37], [251, 155, 6], [247, 209, 61], [252, 255, 164],
55 ];
56 const x = Math.min(0.9999, Math.max(0, t)) * (stops.length - 1);
57 const i = Math.floor(x);
58 const f = x - i;
59 const a = stops[i];
60 const b = stops[i + 1];
61 return [
62 Math.round(a[0] + f * (b[0] - a[0])),
63 Math.round(a[1] + f * (b[1] - a[1])),
64 Math.round(a[2] + f * (b[2] - a[2])),
65 ];
66}