import { useCallback, useEffect, useRef, useState } from "react"; import PyodideWorker from "./pyodide.worker.ts?worker"; import type { ComparisonTrajectory, PlotData, Settings, WorkerMessage } from "./types.ts"; export default function usePyodide(initialSettings: Settings) { const [comparison, setComparison] = useState([]); const [comparisonBusy, setComparisonBusy] = useState(false); const comparisonRequest = useRef(0); const compare = useCallback((settings: Settings[], ids: number[], iteration: number) => { setComparisonBusy(true); workerRef.current?.postMessage({ type: "COMPARE", requestId: ++comparisonRequest.current, settings, ids, iteration }); }, []); const [plotData, setPlotData] = useState({}); const [currentIteration, setCurrentIteration] = useState(0); const [isLoading, setIsLoading] = useState(true); const workerRef = useRef(null); const plotDataRef = useRef({}); const currentIterationRef = useRef(0); function selectIteration(iteration: number) { currentIterationRef.current = iteration; setCurrentIteration(iteration); } const sendInit = useCallback((settings: Settings) => { if (workerRef.current) { workerRef.current.postMessage({ type: "INIT", settings }); } }, []); const sendNextStep = useCallback(() => { const lastPlottedIteration = Math.max((plotDataRef.current.trajectoryValues?.x.length ?? 1) - 1, 0); if (currentIterationRef.current < lastPlottedIteration) { selectIteration(currentIterationRef.current + 1); return; } if (workerRef.current) { workerRef.current.postMessage({ type: "NEXT_STEP" }); } }, []); const sendPrevStep = useCallback(() => { selectIteration(Math.max(currentIterationRef.current - 1, 0)); }, []); const sendReset = useCallback(() => { selectIteration(0); }, []); useEffect(() => { const worker = new PyodideWorker(); workerRef.current = worker; worker.onmessage = (event) => { const message = event.data as WorkerMessage; if (message.type === "COMPARISON_RESULT") { if (message.requestId === comparisonRequest.current) { setComparison(message.trajectories); setComparisonBusy(false); } } else if (message.type === "READY") { console.log("Pyodide is ready"); setIsLoading(false); sendInit(initialSettings); } else if (message.type === "RESULT") { // todo data validation / type setPlotData((prevData) => { const nextData = { functionValues: message.data.functionValues || prevData.functionValues, trajectoryValues: message.data.trajectoryValues || prevData.trajectoryValues, }; plotDataRef.current = nextData; if (message.data.trajectoryValues) { const lastIteration = Math.max(message.data.trajectoryValues.x.length - 1, 0); if (lastIteration <= 0 || lastIteration > currentIterationRef.current) { selectIteration(lastIteration); } } return nextData; }); } }; return () => { worker.terminate(); }; }, []); return { comparison, comparisonBusy, compare, isLoading, plotData, currentIteration, sendInit: sendInit, sendReset: sendReset, sendNextStep: sendNextStep, sendPrevStep: sendPrevStep, } }