"use client"; import { useState, useEffect } from "react"; import ToolShell from "@/components/toolkit/ToolShell"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; function num(v: string) { const n = parseFloat(v); return Number.isFinite(n) ? n : 0; } export default function Page() { const [dpi, setDpi] = useState("300"); const [lpi, setLpi] = useState("300"); const [dpcm, setDpcm] = useState("118.11"); const [active, setActive] = useState<"dpi" | "lpi" | "dpcm">("dpi"); // keep all three in sync based on the most recently edited field useEffect(() => { const D = num(dpi), L = num(lpi), C = num(dpcm); if (active === "dpi") { const d = Math.max(1e-9, D); setDpcm((d / 2.54).toFixed(5)); setLpi(D.toFixed(2)); // LPI≈DPI for raster row spacing (workflow convention) } else if (active === "lpi") { const l = Math.max(1e-9, L); setDpi(L.toFixed(2)); setDpcm((L / 2.54).toFixed(5)); } else { const c = Math.max(1e-9, C); setDpi((c * 2.54).toFixed(2)); setLpi((c * 2.54).toFixed(2)); } }, [dpi, lpi, dpcm, active]); const gapFromDpiMm = 25.4 / Math.max(1e-9, num(dpi)); const gapFromLpiMm = 25.4 / Math.max(1e-9, num(lpi)); return ( Values Derived spacing
Pixel/line gap from DPI
{gapFromDpiMm.toFixed(4)} mm
{(gapFromDpiMm * 1000).toFixed(1)} µm
Line gap from LPI
{gapFromLpiMm.toFixed(4)} mm
{(gapFromLpiMm * 1000).toFixed(1)} µm
Pixels/cm from DPCM
{num(dpcm).toFixed(2)} px/cm
{(num(dpcm) * 2.54).toFixed(2)} px/in
); }