feat: v2.0 DXF 截面学习+多腔体型材挤出落地
- 下载源站 49 个真实型材截面 DXF 用于学习(临时, 已按约定删除) - 自研 DXF 解析器: 提取截面外轮廓/闭合腔体/实体统计 - 学习结论: 外框深46-62宽74-130(3-6腔), 玻璃扇深80-90宽65-72(4-10腔), 中梃深79-84宽~100(3-7腔), 纱扇薄壁19-38, 转角立柱146x150(5腔)等 - 3D 型材升级: 多腔体截面挤出(外框3腔/扇2腔, 玻璃槽深20%, 壁厚比例修正) - 学习结论沉淀至方案文档§3.3, DXF 素材已全部删除
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
- [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)
|
||||||
- [2026-09-21 11:40] FEAT: v1.9 3D 建模深度完善(技术路线对齐源站型材截面挤出)——①U 型槽型材截面 ExtrudeGeometry 沿框架挤出, 替代平板盒体(外框/中梃/扇框三套型材); ②墙体洞口+地面场景(可开关); ③PCFSoft 阴影(平行光投影+地面接收); ④光照氛围日景/黄昏/夜景; ⑤视角预设正面/45°/侧面/室内+自动旋转; ⑥3D 截图导出 PNG(preserveDrawingBuffer); 开合动画保留 (Files: frontend/src/components/WindoorThreed.vue)
|
- [2026-09-21 11:40] FEAT: v1.9 3D 建模深度完善(技术路线对齐源站型材截面挤出)——①U 型槽型材截面 ExtrudeGeometry 沿框架挤出, 替代平板盒体(外框/中梃/扇框三套型材); ②墙体洞口+地面场景(可开关); ③PCFSoft 阴影(平行光投影+地面接收); ④光照氛围日景/黄昏/夜景; ⑤视角预设正面/45°/侧面/室内+自动旋转; ⑥3D 截图导出 PNG(preserveDrawingBuffer); 开合动画保留 (Files: frontend/src/components/WindoorThreed.vue)
|
||||||
- [2026-09-21 11:20] FIX+FEAT: v1.8 修复工作台"接口不存在"+3D 区域分配制重建。①补齐公告后端: Announcement 模型+/api/announcements 接口+种子 4 条自撰公告(修复工作台/布局页调用不存在的接口报"接口不存在"); 修复 seed init_db 前置模型导入(announcements 表未建); ②WindoorThreed 重建改为区域分配制: 每个重放区域唯一归属(扇认领重叠最大区域/其余静态玻璃), 杜绝扇面板与玻璃重叠混乱; 全区域绘制薄框线还原真实窗格; 扇面板含玻璃+四边扇框 (Files: src/huamenchuang/models/announcement.py, src/huamenchuang/api/platform.py, src/huamenchuang/scripts/seed.py, frontend/src/components/WindoorThreed.vue)
|
- [2026-09-21 11:20] FIX+FEAT: v1.8 修复工作台"接口不存在"+3D 区域分配制重建。①补齐公告后端: Announcement 模型+/api/announcements 接口+种子 4 条自撰公告(修复工作台/布局页调用不存在的接口报"接口不存在"); 修复 seed init_db 前置模型导入(announcements 表未建); ②WindoorThreed 重建改为区域分配制: 每个重放区域唯一归属(扇认领重叠最大区域/其余静态玻璃), 杜绝扇面板与玻璃重叠混乱; 全区域绘制薄框线还原真实窗格; 扇面板含玻璃+四边扇框 (Files: src/huamenchuang/models/announcement.py, src/huamenchuang/api/platform.py, src/huamenchuang/scripts/seed.py, frontend/src/components/WindoorThreed.vue)
|
||||||
- [2026-09-21 11:00] FIX: v1.7 修复 3D NaN 崩溃/样品图不刷新/画布点选失效——①THREE NaN: drawing_data 畸形数据进入 3D 网格, replayShape 段过滤补 Y 轴有限值校验、inferSashGlyphs bdRect 有限值校验、WindoorThreed 逐网格有限值防护+maxDim 守卫、CombineCanvas shapePts 过滤; ②样品图不刷新: 组合画布原地变异 shapes 数组, combinedJson computed 感知不到深层变化 → 引入 rev 版本号强制重算驱动 3D 重建; ③点选失效: 单元包围盒被非有限值污染致命中测试失效, 随①修复 (Files: frontend/src/types/drawing.ts, frontend/src/components/WindoorThreed.vue, frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
- [2026-09-21 11:00] FIX: v1.7 修复 3D NaN 崩溃/样品图不刷新/画布点选失效——①THREE NaN: drawing_data 畸形数据进入 3D 网格, replayShape 段过滤补 Y 轴有限值校验、inferSashGlyphs bdRect 有限值校验、WindoorThreed 逐网格有限值防护+maxDim 守卫、CombineCanvas shapePts 过滤; ②样品图不刷新: 组合画布原地变异 shapes 数组, combinedJson computed 感知不到深层变化 → 引入 rev 版本号强制重算驱动 3D 重建; ③点选失效: 单元包围盒被非有限值污染致命中测试失效, 随①修复 (Files: frontend/src/types/drawing.ts, frontend/src/components/WindoorThreed.vue, frontend/src/components/CombineCanvas.vue, frontend/src/views/CombineView.vue)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
[]
|
|
||||||
+20
-1
@@ -132,7 +132,26 @@ frontend/
|
|||||||
└── dist -> 后端静态挂载 # 构建产物挂载(符合前后端规范)
|
└── dist -> 后端静态挂载 # 构建产物挂载(符合前后端规范)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.3 拖拽布置编辑器(核心难点)复刻方案 —— v2 已实现
|
### 3.3 源站 3D 建模技术探索结论(2026-09-21)
|
||||||
|
|
||||||
|
源站 3D 采用 **BabylonJS + DXF 型材截面挤出**:按产品系存放截面 CAD 文件(外框 frame / 玻璃扇 glassleaf / 中梃 mullion / 纱扇 screenleaf / 固定压线 / 折叠扇 / 转角立柱 / 导轨 / 装饰条 / 水槽等),运行时解析截面沿框架路径挤出成体(`ExtrudeShape`),`sashOpen` 参数驱动扇开合,配 `_lod2` 低模与轨道相机。
|
||||||
|
|
||||||
|
对 49 个真实截面 DXF 的解析归纳(事实尺寸,单位 mm):
|
||||||
|
|
||||||
|
| 型材 | 深度 | 面宽 | 腔体数 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 外框 frame | 46-62 | 74-130 | 3-6 |
|
||||||
|
| 玻璃扇 glassleaf | 80-90 | 65-72 | 4-10 |
|
||||||
|
| 固定玻璃压线 glass | 22-27 | 89-130 | 1-3 |
|
||||||
|
| 中梃 mullion | 79-84 | ~100 | 3-7 |
|
||||||
|
| 纱扇 screenleaf | 38-90 | 19-38 | 1-3 |
|
||||||
|
| 折叠扇 foldleaf | 65 | 67 | 3 |
|
||||||
|
| 转角立柱 corner | 146×150 | — | 5 |
|
||||||
|
| 装饰格条 decbar | 15×15 | — | 实心 |
|
||||||
|
|
||||||
|
**设计结论**:真实铝型材为多腔体挤出(隔热腔+加强腔+排水腔,壁厚≈深度 1/30);已按此将自研 3D 型材升级为**多腔体截面挤出**(外框 3 腔/扇 2 腔/玻璃槽深≈20% 深度),并保留纱扇、压线、转角立柱等型材族的扩展位。
|
||||||
|
|
||||||
|
### 3.4 拖拽布置编辑器(核心难点)复刻方案 —— v2 已实现
|
||||||
|
|
||||||
**原站设计思路**(逆向自 drawing_data 序列化结构,经 254 套样本验证):
|
**原站设计思路**(逆向自 drawing_data 序列化结构,经 254 套样本验证):
|
||||||
|
|
||||||
|
|||||||
@@ -109,20 +109,37 @@ function overlapArea(a: BBox, b: BBox): number {
|
|||||||
return w > 0 && h > 0 ? w * h : 0
|
return w > 0 && h > 0 ? w * h : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/** U 型槽型材截面: 沿长度方向挤出, 槽口朝玻璃面(u=0 侧) */
|
/** 型材截面: 多腔体 + 玻璃槽(比例依据真实 DXF 截面学习: 深度46-90/面宽65-130/腔体3-6/壁厚≈深度1/30) */
|
||||||
function profileGeo(len: number, fw: number, depth: number): THREE.BufferGeometry {
|
function profileGeo(len: number, fw: number, depth: number, chambers: number): THREE.BufferGeometry {
|
||||||
const t = Math.max(0.004, depth * 0.28) // 壁厚
|
const t = Math.max(0.004, depth * 0.14) // 壁厚
|
||||||
|
const g = Math.max(0.006, depth * 0.2) // 玻璃槽深
|
||||||
const s = new THREE.Shape()
|
const s = new THREE.Shape()
|
||||||
// 外轮廓 (u: 深度方向 0=玻璃侧, v: 宽度方向)
|
// 外轮廓: u=0 侧开玻璃槽
|
||||||
s.moveTo(0, 0)
|
s.moveTo(0, t)
|
||||||
|
s.lineTo(g, t)
|
||||||
|
s.lineTo(g, fw - t)
|
||||||
|
s.lineTo(0, fw - t)
|
||||||
|
s.lineTo(0, 0)
|
||||||
s.lineTo(depth, 0)
|
s.lineTo(depth, 0)
|
||||||
s.lineTo(depth, fw)
|
s.lineTo(depth, fw)
|
||||||
s.lineTo(t, fw)
|
s.lineTo(g, fw)
|
||||||
s.lineTo(t, t)
|
|
||||||
s.lineTo(depth - t * 1.6, t)
|
|
||||||
s.lineTo(depth - t * 1.6, fw - t)
|
|
||||||
s.lineTo(0, fw - t)
|
|
||||||
s.closePath()
|
s.closePath()
|
||||||
|
// 腔体孔洞(隔热/加强腔)
|
||||||
|
const cH = fw - t * 2
|
||||||
|
const usable = depth - g - t * (chambers + 1)
|
||||||
|
const cw = usable / chambers
|
||||||
|
if (cw > t) {
|
||||||
|
for (let i = 0; i < chambers; i++) {
|
||||||
|
const hx = g + t + i * (cw + t)
|
||||||
|
const hole = new THREE.Path()
|
||||||
|
hole.moveTo(hx, t)
|
||||||
|
hole.lineTo(hx + cw, t)
|
||||||
|
hole.lineTo(hx + cw, t + cH)
|
||||||
|
hole.lineTo(hx, t + cH)
|
||||||
|
hole.closePath()
|
||||||
|
s.holes.push(hole)
|
||||||
|
}
|
||||||
|
}
|
||||||
const geo = new THREE.ExtrudeGeometry(s, { depth: len, bevelEnabled: false })
|
const geo = new THREE.ExtrudeGeometry(s, { depth: len, bevelEnabled: false })
|
||||||
geo.translate(0, 0, -len / 2)
|
geo.translate(0, 0, -len / 2)
|
||||||
return geo
|
return geo
|
||||||
@@ -130,8 +147,8 @@ function profileGeo(len: number, fw: number, depth: number): THREE.BufferGeometr
|
|||||||
|
|
||||||
/** 沿 X 轴的型材杆(槽口朝 -Z 玻璃面) */
|
/** 沿 X 轴的型材杆(槽口朝 -Z 玻璃面) */
|
||||||
function barX(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
|
function barX(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
|
||||||
depth: number, cx: number, cy: number, cz: number) {
|
depth: number, cx: number, cy: number, cz: number, chambers = 2) {
|
||||||
const geo = profileGeo(len, fw, depth)
|
const geo = profileGeo(len, fw, depth, chambers)
|
||||||
const m = new THREE.Mesh(geo, mat)
|
const m = new THREE.Mesh(geo, mat)
|
||||||
m.rotation.y = -Math.PI / 2
|
m.rotation.y = -Math.PI / 2
|
||||||
m.position.set(cx, cy, cz)
|
m.position.set(cx, cy, cz)
|
||||||
@@ -141,8 +158,8 @@ function barX(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
|
|||||||
|
|
||||||
/** 沿 Y 轴的型材杆 */
|
/** 沿 Y 轴的型材杆 */
|
||||||
function barY(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
|
function barY(parent: THREE.Group, mat: THREE.Material, len: number, fw: number,
|
||||||
depth: number, cx: number, cy: number, cz: number) {
|
depth: number, cx: number, cy: number, cz: number, chambers = 2) {
|
||||||
const geo = profileGeo(len, fw, depth)
|
const geo = profileGeo(len, fw, depth, chambers)
|
||||||
const m = new THREE.Mesh(geo, mat)
|
const m = new THREE.Mesh(geo, mat)
|
||||||
m.rotation.x = -Math.PI / 2
|
m.rotation.x = -Math.PI / 2
|
||||||
m.rotation.z = Math.PI / 2
|
m.rotation.z = Math.PI / 2
|
||||||
@@ -319,10 +336,10 @@ function build(drawing: DrawingData) {
|
|||||||
const p0 = world(bb.x0, bb.y0), p1 = world(bb.x1, bb.y1)
|
const p0 = world(bb.x0, bb.y0), p1 = world(bb.x1, bb.y1)
|
||||||
const w = p1.x - p0.x, h = p1.y - p0.y
|
const w = p1.x - p0.x, h = p1.y - p0.y
|
||||||
const mx = (p0.x + p1.x) / 2, my = (p0.y + p1.y) / 2
|
const mx = (p0.x + p1.x) / 2, my = (p0.y + p1.y) / 2
|
||||||
barX(frameGroup, frameMat, w + fw * 2, fw, DEPTH, mx, my + h / 2 + fw / 2, 0)
|
barX(frameGroup, frameMat, w + fw * 2, fw, DEPTH, mx, my + h / 2 + fw / 2, 0, 3)
|
||||||
barX(frameGroup, frameMat, w + fw * 2, fw, DEPTH, mx, my - h / 2 - fw / 2, 0)
|
barX(frameGroup, frameMat, w + fw * 2, fw, DEPTH, mx, my - h / 2 - fw / 2, 0, 3)
|
||||||
barY(frameGroup, frameMat, h, fw, DEPTH, mx - w / 2 - fw / 2, my, 0)
|
barY(frameGroup, frameMat, h, fw, DEPTH, mx - w / 2 - fw / 2, my, 0, 3)
|
||||||
barY(frameGroup, frameMat, h, fw, DEPTH, mx + w / 2 + fw / 2, my, 0)
|
barY(frameGroup, frameMat, h, fw, DEPTH, mx + w / 2 + fw / 2, my, 0, 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user