fix: v2.17 3D 建模对齐样板图——门/纱扇/多扇语义修复
- kind 判定收敛: 仅 Slide=推拉; DoubleSash/DoubleKfcSash=对开门按平开(修门渲染成窗) - 纱扇 Screen 同格不再单独渲染(消除 179 处双层玻璃叠放) - 同格多扇沿宽度等分(forceRect): 31337 对开两扇对半+isDoor 合并, 31729 上排 6 滑扇等分 - 2D 图纸补纱扇网格纹理(官方画法) - 验证: 31337/31729 3D 与官方图结构一致, validate3d 0 无效, ui-shots 6 页无报错
This commit is contained in:
@@ -522,6 +522,25 @@ function drawFrame(
|
||||
}
|
||||
ctx.textBaseline = 'alphabetic'
|
||||
|
||||
// 纱扇(Screen): 官方图纸的网格纹理(不生成开合扇)
|
||||
for (const sub of (shape.sm ?? []) as SashShape[]) {
|
||||
if (String(sub.type ?? '') !== 'Screen' || sub.enabled === false) continue
|
||||
const segs = rp.resolveRef?.(sub.polyId?.idx ?? -1, sub.polyId?.pos)
|
||||
if (!segs) continue
|
||||
const bb = bboxOf(regionPoints(segs))
|
||||
const x0 = tx(bb.x0), y0 = ty(bb.y0), x1 = tx(bb.x1), y1 = ty(bb.y1)
|
||||
ctx.save()
|
||||
ctx.beginPath()
|
||||
ctx.rect(x0, y0, x1 - x0, y1 - y0)
|
||||
ctx.clip()
|
||||
ctx.strokeStyle = 'rgba(60,66,72,0.32)'
|
||||
ctx.lineWidth = 0.7
|
||||
const step = Math.max(5, (x1 - x0) / 22)
|
||||
for (let gx = x0; gx < x1; gx += step) { ctx.beginPath(); ctx.moveTo(gx, y0); ctx.lineTo(gx, y1); ctx.stroke() }
|
||||
for (let gy = y0; gy < y1; gy += step) { ctx.beginPath(); ctx.moveTo(x0, gy); ctx.lineTo(x1, gy); ctx.stroke() }
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
// 悬停格淡高亮(可编辑提示)
|
||||
if (props.editable && hoverRegion && hoverRegion.shapeIdx === shapeIdx
|
||||
&& !(selRegion.value && selRegion.value.shapeIdx === shapeIdx && selRegion.value.ri === hoverRegion.ri)) {
|
||||
|
||||
@@ -336,7 +336,8 @@ function hmOf(sub: SashShape): { openDirection?: string; openToward?: string } {
|
||||
|
||||
/**
|
||||
* 扇样式推断: 有效bd → polyId{idx,pos}挂接区域(原站以区域挂接扇) → 帧bbox按扇数均分兜底。
|
||||
* kind 由类型决定(Slide/DoubleSash=推拉, 其余=平开); 开合五金取 hm.openDirection/hm.openToward。
|
||||
* kind 由类型决定(仅 Slide=推拉, DoubleSash/DoubleKfcSash=双扇对开门按平开); 纱扇(Screen)与
|
||||
* 玻璃扇同格挂接故不单独渲染; 同格多扇沿宽度等分; 开合五金取 hm.openDirection/hm.openToward。
|
||||
*/
|
||||
export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Segment[][]): {
|
||||
regions: Segment[][]
|
||||
@@ -358,8 +359,8 @@ export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Seg
|
||||
const ok = [r.x0, r.y0, r.x1, r.y1].every(v => Number.isFinite(v))
|
||||
return ok && !bd.hidden && r.x1 - r.x0 > 10 && r.y1 - r.y0 > 10 ? r : null
|
||||
}
|
||||
const kindOf = (sub: SashShape) => (sub.type === 'DoubleSash' || sub.type === 'Slide'
|
||||
? 'sliding' as const : 'casement' as const)
|
||||
// kind 仅 Slide=推拉; DoubleSash/DoubleKfcSash=双扇对开门(KfcSash=内开内倒), 均按平开处理
|
||||
const kindOf = (sub: SashShape) => (String(sub.type) === 'Slide' ? 'sliding' as const : 'casement' as const)
|
||||
// polyId{idx,pos} 树寻址; 无树信息时退化为扁平下标
|
||||
const rectOfRef = (ref?: { idx?: number; pos?: number }) => {
|
||||
if (!ref || !Number.isInteger(ref.idx) || (ref.idx as number) < 0) return null
|
||||
@@ -370,18 +371,18 @@ export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Seg
|
||||
const i = ref.idx as number
|
||||
return i < regionBBs.length ? regionBBs[i] : null
|
||||
}
|
||||
const pushGlyphs = (r: { x0: number; y0: number; x1: number; y1: number }, entries: any[], sub: SashShape) => {
|
||||
const pushGlyphs = (r: { x0: number; y0: number; x1: number; y1: number }, entries: any[], sub: SashShape, forceRect = false) => {
|
||||
const kind = kindOf(sub)
|
||||
const hm = hmOf(sub)
|
||||
for (const e of entries) {
|
||||
// 子扇自带挂接区域时优先生效(双扇各自挂半区)
|
||||
const rect = rectOfRef((e as { polyId?: { idx?: number; pos?: number } }).polyId) ?? r
|
||||
// 子扇自带挂接区域时优先生效(双扇各自挂半区); 等分模式下强制使用分块
|
||||
const rect = !forceRect ? (rectOfRef((e as { polyId?: { idx?: number; pos?: number } }).polyId) ?? r) : r
|
||||
glyphs.push({
|
||||
x0: rect.x0, y0: rect.y0, x1: rect.x1, y1: rect.y1,
|
||||
od: (e.od ?? sub.od ?? 90) as number,
|
||||
openToward: (e.openToward ?? sub.openToward ?? hm.openToward) as string | undefined,
|
||||
openDirection: (e.openDirection ?? hm.openDirection) as string | undefined,
|
||||
isDoor: Boolean(e.isDoor ?? sub.isDoor),
|
||||
isDoor: Boolean(e.isDoor || sub.isDoor),
|
||||
wtf: Boolean(e.wtf ?? sub.wtf ?? sub.withTurningFrame),
|
||||
kind,
|
||||
})
|
||||
@@ -389,21 +390,22 @@ export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Seg
|
||||
}
|
||||
const unmatched: { sub: SashShape; entries: any[] }[] = []
|
||||
for (const sub of (shape.sm ?? []) as SashShape[]) {
|
||||
const type = String(sub.type ?? '')
|
||||
if (type === 'Screen' || type === 'DoubleScreen') continue // 纱扇与玻璃扇同格挂接, 不单独渲染
|
||||
const raw = (sub.sashes?.length ? sub.sashes : [sub]) as any[]
|
||||
const entries = raw.filter(e => e && e.enabled !== false)
|
||||
if (!entries.length) continue
|
||||
// 扇数: 多 entries 依 entries 数; Double*(双扇/对开门)取 2
|
||||
const n = Math.max(entries.length, type.startsWith('Double') ? 2 : 1)
|
||||
let rect = sub.bd ? bdRect(sub.bd) : null
|
||||
if (!rect) rect = rectOfRef(sub.polyId)
|
||||
if (rect) {
|
||||
if (entries.length === 2 && !raw.some(e => e?.polyId?.idx != null)) {
|
||||
// 双扇共区且无独立挂接: 沿长边对半
|
||||
const w = rect.x1 - rect.x0, h = rect.y1 - rect.y0
|
||||
const halves = w >= h
|
||||
? [{ ...rect, x1: (rect.x0 + rect.x1) / 2 }, { ...rect, x0: (rect.x0 + rect.x1) / 2 }]
|
||||
: [{ ...rect, y1: (rect.y0 + rect.y1) / 2 }, { ...rect, y0: (rect.y0 + rect.y1) / 2 }]
|
||||
entries.forEach((e, k) => pushGlyphs(halves[k], [e], sub))
|
||||
} else {
|
||||
pushGlyphs(rect, entries, sub)
|
||||
// 同格多扇(对开门/双扇/多滑扇): 沿宽度等分, 每块取对应扇属性
|
||||
for (let k = 0; k < n; k++) {
|
||||
const part = n > 1
|
||||
? { x0: rect.x0 + ((rect.x1 - rect.x0) * k) / n, y0: rect.y0, x1: rect.x0 + ((rect.x1 - rect.x0) * (k + 1)) / n, y1: rect.y1 }
|
||||
: rect
|
||||
pushGlyphs(part, [entries[Math.min(k, entries.length - 1)]], sub, n > 1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -428,7 +430,7 @@ export function inferSashGlyphs(shape: FrameShape, replayed?: ReplayResult | Seg
|
||||
od: (e.od ?? u.sub.od ?? 90) as number,
|
||||
openToward: (e.openToward ?? u.sub.openToward ?? hmOf(u.sub).openToward) as string | undefined,
|
||||
openDirection: (e.openDirection ?? hmOf(u.sub).openDirection) as string | undefined,
|
||||
isDoor: Boolean(e.isDoor ?? u.sub.isDoor),
|
||||
isDoor: Boolean(e.isDoor || u.sub.isDoor),
|
||||
kind: kindOf(u.sub),
|
||||
})
|
||||
i++
|
||||
|
||||
Reference in New Issue
Block a user