import type React from "react"; import { useEffect, useRef, useState } from "react"; import * as papa from "papaparse"; import type { Route } from "./+types/home"; export function meta({ }: Route.MetaArgs) { return [ { title: "3dplot-gen" }, { name: "description", content: "CSV → 3D Plot generator" }, ]; } type Row = Record; export default function Home() { const plotRef = useRef(null); const [rows, setRows] = useState([]); const [columns, setColumns] = useState([]); const [x, setX] = useState(""); const [y, setY] = useState(""); const [z, setZ] = useState(""); // Render plot when axes change useEffect(() => { if (!plotRef.current || !x || !y || !z) return; let cancelled = false; (async () => { const Plotly = await import("plotly.js-dist-min"); if (cancelled) return; const trace = { type: "scatter3d", mode: "markers", x: rows.map(r => r[x]), y: rows.map(r => r[y]), z: rows.map(r => r[z]), marker: { size: 4, color: rows.map(r => r[z]), colorscale: "Viridis", }, }; Plotly.newPlot( plotRef.current!, [trace as any], { paper_bgcolor: "#0b0d12", plot_bgcolor: "#0b0d12", font: { color: "#cfd3ff" }, scene: { xaxis: { title: { text: x } }, yaxis: { title: { text: y } }, zaxis: { title: { text: z } }, }, margin: { l: 0, r: 0, t: 0, b: 0 }, }, { responsive: true } ); })(); return () => { cancelled = true; }; }, [rows, x, y, z]); function onFile(file: File) { papa.parse(file, { header: true, dynamicTyping: true, skipEmptyLines: true, complete: (res) => { const data = res.data as Row[]; setRows(data); setColumns(Object.keys(data[0])); }, }); } async function exportPNG() { const Plotly = await import("plotly.js-dist-min"); if (!plotRef.current) return; Plotly.toImage(plotRef.current, { format: "png", width: 1600, height: 900, }).then((url) => { const a = document.createElement("a"); a.href = url; a.download = "3dplot-gen.png"; a.click(); }); } return (
{/* Sidebar */} {/* Plot */}
); } function Control({ label, children, }: { label: string; children: React.ReactNode; }) { return (
{children}
); }