optimization / frontends /react /src /usePyodide.ts
nanye's picture
support algorithm comparison
603b5b4
Raw History Blame Contribute Delete
3.47 kB
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<ComparisonTrajectory[]>([]);
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<PlotData>({});
const [currentIteration, setCurrentIteration] = useState(0);
const [isLoading, setIsLoading] = useState<boolean>(true);
const workerRef = useRef<Worker | null>(null);
const plotDataRef = useRef<PlotData>({});
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,
}
}