"use client"; import { motion } from "framer-motion"; import { COLORS, VizFrame } from "./common"; // Simple Hungarian-like view: predictions on the left, ground-truth on the right, // with a learned 1-to-1 matching. Unmatched predictions go to "no object". const MATCHES: { p: number; g: number | "none" }[] = [ { p: 0, g: 1 }, { p: 1, g: 0 }, { p: 2, g: "none" }, { p: 3, g: 2 }, { p: 4, g: "none" }, ]; const PRED_LABELS = ["q1", "q2", "q3", "q4", "q5"]; const GT_LABELS = ["gate", "drone", "post"]; export function BipartiteMatching({ width = 720, height = 420, }: { width?: number; height?: number; }) { const W = width; const H = height; const lx = 180; const rx = W - 180; const padY = 60; const sl = (i: number, n: number) => padY + (i + 0.5) * ((H - padY * 2) / n); return ( predictions ground truth ∅ no object {/* Predictions */} {PRED_LABELS.map((p, i) => ( {p} ))} {/* Ground truth */} {GT_LABELS.map((g, i) => ( {g} ))} {/* No object pseudo-node */} {/* Matches */} {MATCHES.map((m, i) => { const py = sl(m.p, PRED_LABELS.length); const gy = m.g === "none" ? H - padY + 4 : sl(m.g, GT_LABELS.length); const gx = m.g === "none" ? rx + 90 : rx; const isMiss = m.g === "none"; return ( ); })} ); }