import { esc, icon, external, section, table, empty, fact, tier, status, thumbnail, enhance, } from "./ui.js"; import { previewHTML, figuresHTML, videoHTML, bindPreview, bindVideo, } from "./preview.js"; export const defaults = { q: "", source: "", method: "", tier: "", status: "", route: "", preview: false, sort: "name", }; const tabs = [ ["overview", "概况"], ["annotation", "采集与标注"], ["access", "数据访问"], ["conversion", "SOMA转换"], ]; function options(list, value, label) { return `${list.map((item) => ``).join("")}`; } export function filterDatasets(context, filters) { const q = filters.q.trim().toLowerCase(); return context.data.datasets .filter((d) => { if ( ["source", "method", "tier", "status", "route"].some( (key) => filters[key] && d[key] !== filters[key], ) ) return false; if (filters.preview && !context.previews[d.id]?.length) return false; const haystack = [ d.name, d.camera, d.gt, d.fills, d.scale, d.subjects, ...(d.tags || []), ] .join(" ") .toLowerCase(); return !q || haystack.includes(q); }) .sort((a, b) => { if (filters.sort === "year") return ( (Number(b.year?.match(/\d{4}/)?.[0]) || 0) - (Number(a.year?.match(/\d{4}/)?.[0]) || 0) || a.name.localeCompare(b.name) ); if (filters.sort === "status") return ( context.meta.status[a.status].order - context.meta.status[b.status].order || a.name.localeCompare(b.name) ); return a.name.localeCompare(b.name, "en", { sensitivity: "base" }); }); } export function catalogView(context, filters) { return `

数据集 ${context.data.datasets.length}

`; } export function bindCatalog(context, filters) { function update() { const datasets = filterDatasets(context, filters); document.getElementById("result-count").textContent = `${datasets.length} / ${context.data.datasets.length}`; document.getElementById("dataset-results").innerHTML = datasets.length ? datasets .map( (d) => `
${thumbnail(d, context)}

${esc(d.name)}

${esc(d.year)}
${esc(context.meta.source[d.source].name)}${esc(context.meta.method[d.method].short)}
${esc(d.gt)}${tier(d.tier, context.meta)}
${esc(d.scale || "—")}${esc(d.subjects)}
${status(d.status, context.meta)}
`, ) .join("") : empty("没有匹配的数据集", true); const params = new URLSearchParams(); Object.entries(filters).forEach(([k, v]) => { if (v && v !== defaults[k]) params.set(k, String(v)); }); history.replaceState( null, "", `#catalog${params.size ? "?" + params : ""}`, ); document.getElementById("dataset-sort").value = filters.sort; enhance(); document.querySelectorAll("[data-clear-filters]").forEach((button) => { button.onclick = reset; }); } function reset() { Object.assign(filters, defaults); document.getElementById("dataset-search").value = ""; document.querySelectorAll("[data-filter]").forEach((el) => { if (el.type === "checkbox") el.checked = false; else el.value = ""; }); update(); } document.getElementById("catalog-filters").onsubmit = (event) => event.preventDefault(); document.getElementById("dataset-search").oninput = (event) => { filters.q = event.target.value; update(); }; document.querySelectorAll("[data-filter]").forEach((el) => { el.onchange = () => { filters[el.dataset.filter] = el.type === "checkbox" ? el.checked : el.value; update(); }; }); document.getElementById("dataset-sort").onchange = (event) => { filters.sort = event.target.value; update(); }; update(); } export function overviewView(context) { const { data, meta } = context; const matrix = data.meta.methods.map( (method) => `${esc(method.short)}${data.meta.sources .map((source) => { const items = data.datasets.filter( (d) => d.method === method.id && d.source === source.id, ); return `${items.map((d) => `${esc(d.name)}`).join("") || '—'}`; }) .join("")}`, ); return `

数据分布

${data.datasets.length} 个数据集
${data.meta.sources.map((source) => `${data.datasets.filter((d) => d.source === source.id).length}${esc(source.name)}`).join("")}
${section("来源 × 标注", table(["标注方法", ...data.meta.sources.map((s) => s.name)], matrix, "matrix"))} ${section("Shape真值", `
${data.meta.tiers.map((t) => `
${tier(t.id, meta)}${esc(t.name)}
${esc(t.desc)}
${data.datasets.filter((d) => d.tier === t.id).length}
`).join("")}
`)} ${section( "优先接入", `
${data.datasets .filter((d) => d.status === "p0") .map( (d) => `${thumbnail(d, context)}

${esc(d.name)}

${esc(d.fills)}

`, ) .join("")}
`, )}
`; } export function detailView(id, tab, context) { const dataset = context.byId[id]; if (!dataset) return empty("未找到这个数据集") + '返回数据集'; const { meta } = context, method = meta.method[dataset.method], route = meta.route[dataset.route]; const links = [...(dataset.links || []), ...(dataset.team || [])]; const body = tab === "annotation" ? `${section(method.name, `
    ${method.steps.map((s) => `
  1. ${esc(s)}
  2. `).join("")}
${fact("可信度", esc(method.trust))}${fact("常见问题", esc(method.issues))}
相关数据集`)}${figuresHTML(id, context)}` : tab === "access" ? section( "来源", `${!links.length ? '

暂无已登记的访问链接。

' : ""}`, ) : tab === "conversion" ? `${section(route.name, `
${fact("原始标注", esc(route.from))}${fact("方法", esc(route.how))}${fact("Shape监督", route.shape ? "可用;仍须核对来源与拟合残差" : "不使用")}${fact("层级", tier(dataset.tier, meta) + " " + esc(meta.tier[dataset.tier].name))}
全部转换路线`)}${id === "egorenderer" ? '

拟合MANO/SOMA是辅助标注,不参与原生手部渲染。

' : ""}` : `${previewHTML(dataset, context)}${section("数据规格", `
${fact("视角与相机", esc(dataset.camera))}${fact("受试者", esc(dataset.subjects))}${fact("规模", esc(dataset.scale))}${fact("原始标注", esc(dataset.gt))}${fact("Shape来源", tier(dataset.tier, meta) + " " + esc(meta.tier[dataset.tier].name))}
${dataset.risk ? `

${esc(dataset.risk)}

` : ""}`)}${videoHTML(id, context)}${!context.previews[id]?.length ? figuresHTML(id, context) : ""}`; return `
数据集

${esc(dataset.name)}

${esc(dataset.fills)}

${status(dataset.status, meta)}${esc(dataset.year)}
${id === "egorenderer" ? '
生产进度读取方法版本
' : ""} ${body}
`; } export function bindDetail(id, context) { if (!context.byId[id]) return; bindPreview(context.byId[id], context); bindVideo(id, context); }