test: v2.18 全量验证适配——254 套样板自动化校验闭环
- 新增 verify-all-samples.mjs: 区域有效/扇矩形/覆盖度/无重叠四类校验(真实编译模块) - 修复 AntiTheft/KfcSash 内倒辅扇与玻璃扇同格叠放: 同格叠扇去重(IoU>0.15 仅首层) - 幽灵形状(空段/无效点)合法跳过 - 终态: 254 样本/409 形状/1247 区域/570 扇失败 0; validate3d 0 无效; 10 类代表样本 3D 抽样比对通过
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
// 全量样板验证适配(使用真实编译模块, 单一事实源): node scripts/verify-all-samples.mjs
|
||||
// 校验: ①重放区域有效 ②扇矩形有效且在窗体内 ③扇与区域覆盖度>=0.55(抓挂接错位) ④扇间无重叠(抓叠放)
|
||||
// 先运行: npx esbuild src/types/drawing.ts --bundle --format=esm --outfile=scripts/.drawing-bundle.mjs
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { parseDrawing, replayShape, inferSashGlyphs } from './.drawing-bundle.mjs'
|
||||
|
||||
const DIR = 'D:/Code/huamenchuang/data/windoor-drawings'
|
||||
const iou = (a, b) => {
|
||||
const w = Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0), h = Math.min(a.y1, b.y1) - Math.max(a.y0, b.y0)
|
||||
if (w <= 0 || h <= 0) return 0
|
||||
const inter = w * h
|
||||
return inter / ((a.x1 - a.x0) * (a.y1 - a.y0) + (b.x1 - b.x0) * (b.y1 - b.y0) - inter)
|
||||
}
|
||||
const cover = (inner, outer) => {
|
||||
const w = Math.min(inner.x1, outer.x1) - Math.max(inner.x0, outer.x0), h = Math.min(inner.y1, outer.y1) - Math.max(inner.y0, outer.y0)
|
||||
if (w <= 0 || h <= 0) return 0
|
||||
return (w * h) / ((inner.x1 - inner.x0) * (inner.y1 - inner.y0))
|
||||
}
|
||||
const bboxOf = pts => ({
|
||||
x0: Math.min(...pts.map(p => p.x)), y0: Math.min(...pts.map(p => p.y)),
|
||||
x1: Math.max(...pts.map(p => p.x)), y1: Math.max(...pts.map(p => p.y)),
|
||||
})
|
||||
|
||||
const files = fs.readdirSync(DIR).filter(f => f.endsWith('.json'))
|
||||
let fail = 0, checked = 0
|
||||
const failures = []
|
||||
const stats = { shapes: 0, regions: 0, sashes: 0, noSash: 0 }
|
||||
for (const f of files) {
|
||||
const row = JSON.parse(fs.readFileSync(path.join(DIR, f), 'utf8'))
|
||||
let dd
|
||||
try { dd = parseDrawing(typeof row.drawing_data === 'string' ? JSON.parse(row.drawing_data) : row.drawing_data) } catch { failures.push([f, 'JSON解析']); fail++; continue }
|
||||
for (let si = 0; si < (dd.sm ?? []).length; si++) {
|
||||
const shape = dd.sm[si]
|
||||
checked++
|
||||
const rp = replayShape(shape)
|
||||
stats.shapes++
|
||||
stats.regions += rp.regions.length
|
||||
const bad = []
|
||||
if (!rp.regions.length) bad.push('无区域')
|
||||
const rbs = rp.regions.map(r => bboxOf(r.map(s => s.ps)))
|
||||
const frameBb = rbs.reduce((a, b) => ({
|
||||
x0: Math.min(a.x0, b.x0), y0: Math.min(a.y0, b.y0), x1: Math.max(a.x1, b.x1), y1: Math.max(a.y1, b.y1),
|
||||
}), { x0: Infinity, y0: Infinity, x1: -Infinity, y1: -Infinity })
|
||||
for (const rb of rbs) {
|
||||
const area = (rb.x1 - rb.x0) * (rb.y1 - rb.y0)
|
||||
if (!Number.isFinite(area) || area <= 0) bad.push('无效区域')
|
||||
}
|
||||
const sashes = inferSashGlyphs(shape, rp).sashes
|
||||
stats.sashes += sashes.length
|
||||
if (!sashes.length) stats.noSash++
|
||||
// 无几何且无扇的幽灵形状(序列化残留, 如空段/无效点)合法跳过
|
||||
if (!rp.regions.length && !sashes.length) { bad.length = 0; continue }
|
||||
sashes.forEach((s, k) => {
|
||||
const rect = { x0: s.x0, y0: s.y0, x1: s.x1, y1: s.y1 }
|
||||
const w = rect.x1 - rect.x0, h = rect.y1 - rect.y0
|
||||
if (!(w > 0 && h > 0 && Number.isFinite(w) && Number.isFinite(h))) { bad.push(`扇${k}(${s.kind})矩形无效`); return }
|
||||
if (cover(rect, frameBb) < 0.98) bad.push(`扇${k}越出窗体`)
|
||||
const bestCov = Math.max(...rbs.map(rb => cover(rect, rb)))
|
||||
if (bestCov < 0.55) bad.push(`扇${k}覆盖度${bestCov.toFixed(2)}`)
|
||||
sashes.forEach((o, j) => {
|
||||
if (j <= k) return
|
||||
const or = { x0: o.x0, y0: o.y0, x1: o.x1, y1: o.y1 }
|
||||
if (iou(rect, or) > 0.15) bad.push(`扇${k}×扇${j}重叠`)
|
||||
})
|
||||
})
|
||||
if (bad.length) { failures.push([f, `shape${si}: ${bad.join('; ')}`]); fail++ }
|
||||
}
|
||||
}
|
||||
console.log(`样本: ${files.length} | 形状: ${stats.shapes} | 区域: ${stats.regions} | 扇: ${stats.sashes} | 无扇形状: ${stats.noSash}`)
|
||||
console.log(`失败项: ${fail}`)
|
||||
failures.slice(0, 50).forEach(([f, msg]) => console.log(' ✗', f, msg))
|
||||
if (failures.length > 50) console.log(` ...共 ${failures.length} 项`)
|
||||
Reference in New Issue
Block a user