import { useEffect, useRef } from "react"; import { magma } from "../viz/magma"; type FieldResult = { grid: { nx: number; ny: number; dx: number; dy: number }; temperature: number[][]; pressure: number[][]; t_min: number; t_max: number; p_min_MPa: number; p_max_MPa: number; elapsed_seconds: number; }; export function FieldPlot({ title, result, tMin, tMax, }: { title: string; result: FieldResult; tMin: number; tMax: number; }) { const ref = useRef(null); useEffect(() => { const canvas = ref.current; if (!canvas) return; const nx = result.grid.nx; const ny = result.grid.ny; canvas.width = nx; canvas.height = ny; const ctx = canvas.getContext("2d"); if (!ctx) return; const img = ctx.createImageData(nx, ny); const range = tMax - tMin || 1; for (let j = 0; j < ny; j++) { const jSrc = ny - 1 - j; // flip so origin=bottom-left for (let i = 0; i < nx; i++) { const v = result.temperature[i]?.[jSrc] ?? tMin; const [r, g, b] = magma((v - tMin) / range); const idx = (j * nx + i) * 4; img.data[idx] = r; img.data[idx + 1] = g; img.data[idx + 2] = b; img.data[idx + 3] = 255; } } ctx.putImageData(img, 0, 0); }, [result, tMin, tMax]); const extentX = result.grid.nx * result.grid.dx; const extentY = result.grid.ny * result.grid.dy; return (

{title}

{result.elapsed_seconds.toFixed(2)} s

T {result.t_min.toFixed(1)}–{result.t_max.toFixed(1)}°C · P{" "} {result.p_min_MPa.toFixed(2)}–{result.p_max_MPa.toFixed(2)} MPa · extent{" "} {(extentX / 1000).toFixed(1)}×{(extentY / 1000).toFixed(1)} km

); } function Colorbar({ tMin, tMax }: { tMin: number; tMax: number }) { const stops = Array.from({ length: 32 }, (_, i) => i / 31); return (
{stops.map((t, i) => { const [r, g, b] = magma(t); return (
); })}
{tMin.toFixed(0)}°C {tMax.toFixed(0)}°C
); }