From 5eb93f043e2f01668d08fddc699b73144980c7ad Mon Sep 17 00:00:00 2001 From: HHY Date: Mon, 21 Sep 2026 04:08:56 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20v2.2=20=E4=BF=AE=E5=A4=8D=203D=20?= =?UTF-8?q?=E6=88=AA=E5=9B=BE=E7=BA=AF=E8=89=B2=E7=A9=BA=E7=99=BD(?= =?UTF-8?q?=E5=A2=99=E4=BD=93=E5=B0=BA=E5=AF=B8=E5=8D=95=E4=BD=8D=E9=94=99?= =?UTF-8?q?=E8=AF=AF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根因: 墙体宽度 bw=1/S*0.72 误用模型单位(≈最大边长*0.72, 数千单位) 而放置坐标是世界单位(窗体≈1), 相机被包在巨墙内只见背景色 - 改为世界单位: 上下墙覆盖窗洞±2.2x0.55, 左右墙 0.55x窗高 - 墙体不投影(避免自遮挡), 地面尺寸同步修正 - Node 验证四墙尺寸全部 ≤2.2 世界单位 --- 3D窗型.png | Bin 0 -> 4672 bytes CHANGELOG | 1 + frontend/src/components/WindoorThreed.vue | 26 +++++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 3D窗型.png diff --git a/3D窗型.png b/3D窗型.png new file mode 100644 index 0000000000000000000000000000000000000000..83b843ba02bc35b913c60512f9e740f025af146c GIT binary patch literal 4672 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 literal 0 HcmV?d00001 diff --git a/CHANGELOG b/CHANGELOG index 2fd26f8..c6b7ad4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # CHANGELOG +- [2026-09-21 12:25] FIX: v2.2 修复 3D 截图为纯色空白——墙体尺寸单位错误(bw=1/S*0.72 误用模型单位≈2304, 而放置坐标是世界单位±0.5), 相机被包在数千单位宽的巨墙内只见背景色; 改为世界单位(侧墙 0.55, 上下墙覆盖窗洞±2.2), 墙体不投影(避免自身遮挡), Node 验证四墙尺寸全部 ≤2.2 世界单位 (Files: frontend/src/components/WindoorThreed.vue) - [2026-09-21 12:10] FIX: 3D"解析失败"根因修复+错误可见性——①根因: watch immediate 首跑时 camera 未创建而 build() 触碰 camera 抛 TypeError; 移除 build 内相机定位(每帧 loop 已定位); ②shadowMap.type 赋值整行移除(three r186 已移除 PCFSoft 常量, 默认即 PCFShadowMap, 杜绝弃用警告); ③解析失败时显示真实错误信息(info+console.error), 便于后续定位; 重建 dist 后 PCFSoft 赋值触发路径已清零 (Files: frontend/src/components/WindoorThreed.vue) - [2026-09-21 11:30] FIX: v2.1 3D 渲染问题根治——①profileGeo 外轮廓路径自相交(闭合斜边穿过槽口区域)导致三角剖分破面, 重写为正确顶点顺序的 U 槽简单多边形; ②inferSashGlyphs 兜底 bboxOf 误传段对象(应为点对象)产生 NaN 扇矩形(JSON 序列化为 null), 修正为有限值点集; ③型材截面挤出抽为共享模块 three/profileGeo.ts(geometryIsValid 校验器); ④新增 scripts/validate3d.cjs 全量验证工具: 254 样板×3700 网格×798 扇逐网格 NaN 检测全部通过; ⑤过小面板(窄条扇)为合法数据跳过渲染不视为失败 (Files: frontend/src/components/WindoorThreed.vue, frontend/src/three/profileGeo.ts, frontend/src/types/drawing.ts, frontend/scripts/validate3d.cjs) - [2026-09-21 11:55] FEAT: v2.0 DXF 截面学习+多腔体型材落地。①下载源站 49 个真实型材截面 DXF(8 产品系×外框/玻璃扇/中梃/纱扇/压线/转角立柱等); ②自研 DXF 解析器提取截面结构: 归纳出各型材深度/面宽/腔体数区间(外框46-62深3-6腔, 玻璃扇80-90深4-10腔, 中梃79-84深3-7腔等); ③3D 型材升级为多腔体截面挤出(外框3腔/扇2腔, 玻璃槽深20%, 壁厚≈深度14%); ④学习结论沉淀至方案文档§3.3; ⑤按约定删除全部 DXF 素材与解析脚本 (Files: frontend/src/components/WindoorThreed.vue, doc/克隆实施方案.md) diff --git a/frontend/src/components/WindoorThreed.vue b/frontend/src/components/WindoorThreed.vue index 8ac9e95..aa90018 100644 --- a/frontend/src/components/WindoorThreed.vue +++ b/frontend/src/components/WindoorThreed.vue @@ -308,30 +308,34 @@ function build(drawing: DrawingData) { } } - // ---- 墙体洞口 + 地面 ---- + // ---- 墙体洞口 + 地面(世界单位:窗体约 1 单位见方)---- if (showWall.value) { - const bw = 1 / S * 0.72, bh = 1 / S * 0.72 - const wx0 = -gw * S / 2 - 0.06, wx1 = gw * S / 2 + 0.06 - const wy0 = -gh * S / 2 - 0.04, wy1 = gh * S / 2 + 0.06 + const hw = gw * S / 2 + 0.05 // 窗洞半宽 + const hh = gh * S / 2 + 0.05 // 窗洞半高 + const side = 0.55 // 上下墙高度/左右墙宽度 const wt = DEPTH * 3 const wallBoxes: [number, number, number, number, number][] = [ - [wx0 - bw, wy1, bw * 2, bh], // 上 - [wx0 - bw, wy0 - bh, bw * 2, bh], // 下 - [wx0 - bw, wy0, bw, wy1 - wy0], // 左 - [wx1, wy0, bw, wy1 - wy0], // 右 + [-hw - side, hh, side * 2 + hw * 2, side], // 上 + [-hw - side, -hh - side, side * 2 + hw * 2, side], // 下 + [-hw - side, -hh, side, hh * 2], // 左 + [hw, -hh, side, hh * 2], // 右 ] for (const [px, py, pw, ph] of wallBoxes) { const m = new THREE.Mesh(new THREE.BoxGeometry(pw, ph, wt), wallMat) m.position.set(px + pw / 2, py + ph / 2, 0) m.receiveShadow = true + m.userData.isWall = true sceneRoot.add(m) } - const ground = new THREE.Mesh(new THREE.BoxGeometry(bw * 4, 0.02, DEPTH * 8), groundMat) - ground.position.set(0, wy0 - 0.02, DEPTH * 2) + const ground = new THREE.Mesh(new THREE.BoxGeometry(3, 0.02, DEPTH * 8), groundMat) + ground.position.set(0, -hh - side - 0.02, DEPTH * 2) ground.receiveShadow = true sceneRoot.add(ground) } - sceneRoot.traverse(o => { if ((o as THREE.Mesh).isMesh) { o.castShadow = true; o.receiveShadow = true } }) + sceneRoot.traverse(o => { + const m = o as THREE.Mesh + if (m.isMesh) { m.castShadow = !m.userData.isWall; m.receiveShadow = true } + }) // 玻璃不投影 sceneRoot.traverse(o => { const mat = (o as THREE.Mesh).material as THREE.Material | undefined