import {dataUrl} from './images.mjs'; const escape = (value) => String(value).replace(/[&<>"']/gu, (c) => ({'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}[c])); export function annotatedSvg(image, parts, geometry = null) { const {width: w, height: h} = image; const stroke = Math.max(w, h) / 400; const font = Math.max(w, h) / 65; const byId = new Map(parts.map((part) => [part.id, part])); const diagnostics = new Map((geometry?.parts || []).map((part) => [part.id, part])); const overlays = parts.map((part, index) => { const color = `hsl(${(index * 137.5) % 360} 85% 55%)`; const [px, py] = [part.pivot_norm[0] * w, part.pivot_norm[1] * h]; const parent = byId.get(part.parent); const line = parent ? `` : ''; let box = ''; if (part.bbox_norm_xyxy) { const [x0, y0, x1, y1] = part.bbox_norm_xyxy; box = ``; } const diagnostic = diagnostics.get(part.id); const points = part.positive_points_norm.map(([x, y], i) => { const status = diagnostic?.model_positive_points[i]?.status; const fill = status === 'INVALID_SEED' ? '#ff4444' : status === 'UNTRUSTED_SEED' ? '#ffc857' : '#999'; return `${escape(part.id)}: ${status || 'UNTRUSTED_MODEL_POINT'}`; }).join(''); const candidates = (diagnostic?.candidate_points || []).map(({point_norm: [x, y]}) => `${escape(part.id)}: Alpha candidate, semantic unverified`).join(''); return `${line}${box}${points}${candidates}${escape(part.id)}`; }).join('\n'); return `${overlays}`; } export function understandingReport(image, data, validation, metadata, geometry = null) { const svg = data && validation.valid ? annotatedSvg(image, data.parts, geometry) : `source`; const diagnostics = new Map((geometry?.parts || []).map((part) => [part.id, part])); const rows = data && validation.valid ? data.parts.map((part) => { const g = diagnostics.get(part.id); return `${escape(part.id)}${escape(part.rig_role || '')}
${escape(part.bind_to ?? part.parent ?? 'root')}${escape(part.visibility)}${escape(part.occluded_by.join(', '))}${escape(Object.entries(part.completion || {}).filter(([, value]) => value).map(([key]) => key).join(', ') || '无')}${g?.bbox_foreground_ratio == null ? '—' : `${(g.bbox_foreground_ratio * 100).toFixed(1)}%`}${escape(g?.status || 'pending')}
${escape((g?.warnings || []).join(', '))}${part.confidence}`; }).join('') : ''; return `角色结构与几何检查

角色结构与几何检查

尚未执行 SAM 分割或补全。红色实心圆:无效模型点;黄色实心圆:通过前景检查但不可信的模型点;青色方块:程序生成的 Alpha 候选。空心圆是原始关节点,不做前景吸附。粗框与候选均需语义复核,Alpha 不能证明点属于目标部件。

状态:${validation.valid ? 'needs_review(待检查)' : 'failed'}

${svg}

几何摘要

${escape(JSON.stringify(geometry?.summary || {}, null, 2))}

部件

${rows}
ID角色/绑定可见性遮挡物补全项框内前景候选状态自报置信度

校验

${escape(JSON.stringify(validation, null, 2))}

模型说明

${escape(JSON.stringify(data?.notes || [], null, 2))}

请求记录

${escape(JSON.stringify(metadata, null, 2))}
`; }