feat: 渲染视觉与图纸对齐(源站原生扇内结构全量消费: 扇内分割/裙板/格条/编号, inferSashPanels 工具+三渲染器接入+锚点归属匹配+对话框ResizeObserver)
This commit is contained in:
@@ -67,6 +67,7 @@ export interface SashShape {
|
||||
openToward?: string // inward/outward
|
||||
rt?: number | boolean // 圆弧半径(弧顶扇)
|
||||
sashes?: SashEntry[] // DoubleSash 的子扇
|
||||
mm?: { sp?: SplitLine[]; dbs?: unknown; fls?: { fls?: any[] }; phs?: unknown[]; lpfs?: unknown[] } // 扇内分割/玻璃/格条(源站原生)
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
@@ -480,3 +481,53 @@ export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Seg
|
||||
}
|
||||
return { regions, sashes: glyphs }
|
||||
}
|
||||
|
||||
/** 扇内子格(源站原生结构消费):扇自身 mm.sp 内部分割 + mm.fls 玻璃挂接 + mm.dbs.bars 源站格条。
|
||||
* 扇无基底 polygon(几何=挂接区域矩形),以 rect 构造临时 shape 复用 replayShape 全语义重放。 */
|
||||
export interface SashPanel {
|
||||
x0: number; y0: number; x1: number; y1: number
|
||||
glass: boolean // true=玻璃格(挂 fls 或无分割默认) / false=实板(门裙板等)
|
||||
glassText?: string // 玻璃编号(ser.text)
|
||||
glassSpec?: string // 玻璃配置(spet.sp)
|
||||
deco?: { tp: string; gp: number; hc: number } // 源站格条(dbs.bars: tp=风格, gp=回纹尺寸 mm, hc=主格等分数)
|
||||
}
|
||||
|
||||
export function inferSashPanels(sash: SashShape, rect: { x0: number; y0: number; x1: number; y1: number }): SashPanel[] {
|
||||
const mm = ((sash as Record<string, unknown>).mm ?? {}) as { sp?: SplitLine[]; fls?: { fls?: any[] }; dbs?: unknown }
|
||||
const sps = mm.sp ?? []
|
||||
const fls = mm.fls?.fls ?? []
|
||||
if (!sps.length) {
|
||||
const fl = fls[0]
|
||||
return [{ ...rect, glass: true, glassText: fl?.ser?.text, glassSpec: fl?.spet?.sp }]
|
||||
}
|
||||
const ring = [
|
||||
[{ ps: { x: rect.x0, y: rect.y0 }, pe: { x: rect.x1, y: rect.y0 } },
|
||||
{ ps: { x: rect.x1, y: rect.y0 }, pe: { x: rect.x1, y: rect.y1 } },
|
||||
{ ps: { x: rect.x1, y: rect.y1 }, pe: { x: rect.x0, y: rect.y1 } },
|
||||
{ ps: { x: rect.x0, y: rect.y1 }, pe: { x: rect.x0, y: rect.y0 } }],
|
||||
]
|
||||
const fake = { ...(sash as Record<string, unknown>), polygon: { type: 'polygon', polygon: ring, spLine: null, virtual: false }, mm } as unknown as FrameShape
|
||||
const rp = replayShape(fake)
|
||||
const decos: { pid: { idx: number; pos: number }; tp: string; gp: number; hc: number }[] = []
|
||||
const barsRaw = (mm.dbs && typeof mm.dbs === 'object' ? (mm.dbs as { bars?: unknown[] }).bars : (mm.dbs as unknown[])) ?? []
|
||||
for (const entry of barsRaw as unknown[]) {
|
||||
try {
|
||||
const pair = entry as [string, { db?: { tp?: string; gp?: number } } | undefined]
|
||||
const pid = typeof pair[0] === 'string' ? JSON.parse(pair[0]) : pair[0]
|
||||
const db = (pair[1]?.db ?? pair[1]) as { tp?: string; gp?: number; hc?: number } | undefined
|
||||
if (pid && Number.isInteger(pid.idx)) decos.push({ pid, tp: String(db?.tp ?? ''), gp: Number(db?.gp) || 80, hc: Number(db?.hc) || 2 })
|
||||
} catch { /* 坏数据忽略 */ }
|
||||
}
|
||||
return rp.regions.map((segs, i) => {
|
||||
const bb = bboxOf(regionPoints(segs))
|
||||
const ref = rp.leafRefs[i] ?? { idx: -1, pos: -1 }
|
||||
const fl = fls.find(f => { const p = f?.pid ?? {}; return p.idx === ref.idx && p.pos === ref.pos })
|
||||
?? (fls.length === 1 && (fls[0]?.pid?.idx ?? -1) < 0 ? fls[0] : undefined)
|
||||
const isPanel = String(fl?.type ?? '') === 'Panel'
|
||||
const deco = decos.find(d => d.pid.idx === ref.idx && d.pid.pos === ref.pos)
|
||||
return { x0: bb.x0, y0: bb.y0, x1: bb.x1, y1: bb.y1,
|
||||
glass: isPanel ? false : Boolean(fl) || fls.length === 0,
|
||||
glassText: fl?.ser?.text, glassSpec: fl?.spet?.sp,
|
||||
deco: deco ? { tp: deco.tp, gp: deco.gp, hc: deco.hc } : undefined }
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user