From 97f5c78907f6d352677d06c5dd5e6c4c4b7d1ff6 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Thu, 7 May 2026 17:09:18 +0800 Subject: [PATCH] =?UTF-8?q?2026-05-07-17-05-43=20=E5=A2=9E=E5=8A=A03D?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E6=98=BE=E7=A4=BA=E6=A1=A3=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebSite/server.ts | 2 +- WebSite/src/components/ProjectLibrary.tsx | 18 ++++--- 工程分析/实现方案-2026-05-07-17-05-43.md | 64 +++++++++++++++++++++++ 工程分析/测试方案-2026-05-07-17-05-43.md | 44 ++++++++++++++++ 工程分析/经验记录.md | 18 +++++++ 工程分析/需求分析-2026-05-07-17-05-43.md | 50 ++++++++++++++++++ 6 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 工程分析/实现方案-2026-05-07-17-05-43.md create mode 100644 工程分析/测试方案-2026-05-07-17-05-43.md create mode 100644 工程分析/需求分析-2026-05-07-17-05-43.md diff --git a/WebSite/server.ts b/WebSite/server.ts index b7de5c7..a53610d 100644 --- a/WebSite/server.ts +++ b/WebSite/server.ts @@ -701,7 +701,7 @@ function createStlPreview(filePath: string, fileName: string, limit: number) { throw new Error('当前仅支持二进制 STL 预览'); } - const sampleLimit = Math.max(100, Math.min(limit, 72000)); + const sampleLimit = Math.max(100, Math.min(limit, 200000)); const step = Math.max(1, Math.ceil(triangleCount / sampleLimit)); const vertices: number[] = []; let sampledTriangles = 0; diff --git a/WebSite/src/components/ProjectLibrary.tsx b/WebSite/src/components/ProjectLibrary.tsx index df7ab75..8d4920b 100644 --- a/WebSite/src/components/ProjectLibrary.tsx +++ b/WebSite/src/components/ProjectLibrary.tsx @@ -26,7 +26,7 @@ import { api, downloadDicomArchive, downloadMask } from '../lib/api'; type Plane = 'axial' | 'sagittal' | 'coronal'; type DisplayMode = DicomPreview['mode']; -type SolidityLevel = 'preview' | 'standard' | 'fine' | 'ultra'; +type SolidityLevel = 'standard' | 'fine' | 'ultra' | 'solid'; interface ModuleStyle { visible: boolean; @@ -59,10 +59,10 @@ type ModelPoseKey = keyof ModelPose; const defaultModuleColors = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#14b8a6', '#f97316', '#64748b', '#ec4899']; const solidityOptions: Array<{ id: SolidityLevel; label: string; limit: number }> = [ - { id: 'preview', label: '预览', limit: 6000 }, { id: 'standard', label: '标准', limit: 16000 }, { id: 'fine', label: '精细', limit: 36000 }, { id: 'ultra', label: '超精细', limit: 72000 }, + { id: 'solid', label: '实体', limit: 200000 }, ]; const defaultModelPose: ModelPose = { rotateX: 0, @@ -291,6 +291,7 @@ function NativeStlViewer({ files, styles, detailLimit, + solidMode, pose, onPoseChange, }: { @@ -298,6 +299,7 @@ function NativeStlViewer({ files: string[]; styles: Record; detailLimit: number; + solidMode: boolean; pose: ModelPose; onPoseChange: React.Dispatch>; }) { @@ -491,13 +493,14 @@ function NativeStlViewer({ geometry.setAttribute('position', new THREE.Float32BufferAttribute(payload.vertices, 3)); geometry.computeVertexNormals(); const style = styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true }; + const materialOpacity = solidMode ? Math.max(style.opacity, 0.94) : style.opacity; const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial({ color: style.color, - opacity: style.opacity, - transparent: style.opacity < 1, - roughness: 0.42, + opacity: materialOpacity, + transparent: materialOpacity < 1, + roughness: solidMode ? 0.56 : 0.42, metalness: 0.04, side: THREE.DoubleSide, }), @@ -600,7 +603,7 @@ function NativeStlViewer({ }); container.innerHTML = ''; }; - }, [projectId, files.join('|'), JSON.stringify(styles), detailLimit]); + }, [projectId, files.join('|'), JSON.stringify(styles), detailLimit, solidMode]); return (
@@ -696,7 +699,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri ]; const allModulesVisible = stlFiles.length > 0 && stlFiles.every((file) => moduleStyles[file]?.visible !== false); const sliceTotal = dicomPreview?.total ?? selectedProject?.dicomCount ?? 0; - const selectedSolidity = solidityOptions.find((option) => option.id === solidityLevel) ?? solidityOptions[1]; + const selectedSolidity = solidityOptions.find((option) => option.id === solidityLevel) ?? solidityOptions[0]; useEffect(() => { const next: Record = {}; @@ -1235,6 +1238,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri files={stlFiles} styles={moduleStyles} detailLimit={selectedSolidity.limit} + solidMode={solidityLevel === 'solid'} pose={modelPose} onPoseChange={setModelPose} /> diff --git a/工程分析/实现方案-2026-05-07-17-05-43.md b/工程分析/实现方案-2026-05-07-17-05-43.md new file mode 100644 index 0000000..9be8b83 --- /dev/null +++ b/工程分析/实现方案-2026-05-07-17-05-43.md @@ -0,0 +1,64 @@ +# 实现方案 - 2026-05-07-17-05-43 + +## 修改目标 + +将 3D 模型显示档位由 `预览 / 标准 / 精细 / 超精细` 改为 `标准 / 精细 / 超精细 / 实体`,并让 `实体` 更接近完整 STL 面片渲染。 + +## 涉及路径 + +- `WebSite/src/components/ProjectLibrary.tsx` +- `WebSite/server.ts` + +## 技术路线 + +### 1. 更新档位类型和配置 + +- 将 `SolidityLevel` 从 `preview | standard | fine | ultra` 改为 `standard | fine | ultra | solid`。 +- 删除 `预览` 选项。 +- 新增 `实体` 选项。 +- 设置 `实体` 的 `limit` 为更高值,尽量覆盖默认 9 个 STL 的完整三角面。 + +### 2. 提升后端 STL preview 上限 + +- 后端 `createStlPreview()` 当前最大抽样上限为 `72000`。 +- 将上限提升到 `200000`,支持实体档位请求更多三角面。 +- 继续保留 `sampleLimit` clamp,避免异常超大请求拖垮服务。 + +### 3. 优化实体档位材质 + +- 在 `NativeStlViewer` 中判断当前档位是否为 `solid`。 +- `solid` 档位下使用更高的不透明度下限,例如 `0.92`,增强实体感。 +- 其他档位继续使用构件层级中的用户透明度设置。 + +## 数据流或交互流程 + +1. 用户点击 `实体`。 +2. 前端 `solidityLevel` 切换为 `solid`。 +3. `selectedSolidity.limit` 变大,`NativeStlViewer` 重新请求 STL preview。 +4. 后端按更高上限返回更多三角面和稳定 `bounds`。 +5. 前端以实体材质重新渲染模型。 + +## 兼容性与回滚方案 + +- 若实体档位渲染压力过大,可降低 `solid.limit` 或后端最大上限。 +- 若用户仍需要低质量快速预览,可恢复 `preview` 配置。 + +## 预计文件变更 + +- `WebSite/src/components/ProjectLibrary.tsx` + - 更新 `SolidityLevel`、`solidityOptions`。 + - `NativeStlViewer` 增加 `solidMode` 参数。 + - 实体档位材质增强不透明度。 +- `WebSite/server.ts` + - 提升 STL preview 最大抽样上限。 + +## 人工审核状态 + +- 本次免二次确认,方案写入后直接执行。 + +## 执行结果 + +- 已删除 `模型显示` 中的 `预览` 档位。 +- 已新增 `实体` 档位,前端请求上限为 `200000`。 +- 已将后端 STL preview 最大抽样上限从 `72000` 提升到 `200000`。 +- 已在实体档位提高材质不透明度下限,增强实体面片视觉。 diff --git a/工程分析/测试方案-2026-05-07-17-05-43.md b/工程分析/测试方案-2026-05-07-17-05-43.md new file mode 100644 index 0000000..2c94daf --- /dev/null +++ b/工程分析/测试方案-2026-05-07-17-05-43.md @@ -0,0 +1,44 @@ +# 测试方案 - 2026-05-07-17-05-43 + +## 静态检查 + +1. `git status --short --branch` +2. `cd WebSite && npm run build` +3. `cd WebSite && npm run lint` + +## 单元或集成测试 + +当前项目没有独立单元测试体系,本次采用构建、类型检查、API 冒烟和页面人工验证。 + +## 关键业务场景验证 + +1. 打开 `http://192.168.3.11:4000/`。 +2. 进入 `项目库 - 3D 模型`。 +3. 验证 `模型显示` 中不再出现 `预览`。 +4. 验证 `模型显示` 中出现 `实体`。 +5. 点击 `标准 / 精细 / 超精细 / 实体`: + - 模型都可以加载完成。 + - 当前位姿不丢失。 + - 实体档位视觉上更接近面片实体,而不是稀疏预览。 +6. 验证右侧构件透明度、颜色、眼睛显示控制仍可用。 + +## 医学影像数据相关边界验证 + +- 本次不修改 DICOM 解析、切片显示、分割结果导出。 +- 回归确认 `/api/projects` 正常返回默认项目。 + +## 回归风险 + +- 实体档位请求更多 STL 顶点,首次加载可能更慢。 +- 若浏览器 WebGL 性能不足,实体档位可能出现卡顿。 + +## 人工审核状态 + +- 本次免二次确认。 + +## 执行记录 + +- `npm run build`:通过。 +- `npm run lint`:通过,实际执行 `tsc --noEmit`。 +- 重新部署后 `curl -I http://127.0.0.1:4000/`:返回 `HTTP/1.1 200 OK`。 +- 重新部署后请求 `会厌.stl` preview 且 `limit=200000`:返回 `triangleCount=17384`、`sampledTriangles=17384`,确认实体档位可返回完整三角面。 diff --git a/工程分析/经验记录.md b/工程分析/经验记录.md index 35c5a29..1d93a73 100644 --- a/工程分析/经验记录.md +++ b/工程分析/经验记录.md @@ -613,3 +613,21 @@ C. 解决问题方案 D. 后续如何避免问题 任何用于配准、视角稳定、旋转中心的几何参数都必须来自全量模型或稳定元数据,不能来自随性能档位变化的抽样数据;坐标系、状态文字和模型实际位姿应共用同一份 pose 状态。 + +## 2026-05-07-17-05-43 模型显示实体档位 + +A. 具体问题 + +用户不再需要低质量 `预览` 档位,希望在 `模型显示` 中增加更接近 STL 实体面片的 `实体` 档位。 + +B. 产生问题原因 + +原有档位以性能抽样为主,最高 `超精细` 仍受后端 `72000` 三角面上限约束,且材质继续使用用户设置的半透明度,视觉上仍偏点状或半透明预览。 + +C. 解决问题方案 + +前端删除 `预览` 档位,新增 `实体` 档位并设置更高请求上限 `200000`;后端 STL preview clamp 同步提升到 `200000`;实体档位下将材质不透明度下限提高到 `0.94`,在保留构件颜色的同时增强实体感。 + +D. 后续如何避免问题 + +模型显示档位应明确区分“性能抽样”和“实体查看”;若新增高质量档位,需要同步检查前端请求 limit、后端 clamp、材质表现和浏览器性能边界。 diff --git a/工程分析/需求分析-2026-05-07-17-05-43.md b/工程分析/需求分析-2026-05-07-17-05-43.md new file mode 100644 index 0000000..8efa3c6 --- /dev/null +++ b/工程分析/需求分析-2026-05-07-17-05-43.md @@ -0,0 +1,50 @@ +# 需求分析 - 2026-05-07-17-05-43 + +## 原始需求摘要 + +用户要求在 `项目库 - 3D 模型` 的 `模型显示` 中: + +1. 去掉 `预览` 档位。 +2. 增加 `实体` 档位。 +3. 本次需求分析、实现方案、测试方案、执行修改都不需要人工二次确认。 + +## 业务目标 + +- 让模型显示档位更符合当前用户对实体化浏览的期望。 +- 减少低质量 `预览` 档位对视觉判断的干扰。 +- 提供更接近 STL 实体面的模型显示效果。 + +## 输入与输出 + +输入: + +- 用户在 3D 模型页点击 `模型显示` 档位。 + +输出: + +- 档位变为 `标准 / 精细 / 超精细 / 实体`。 +- `实体` 档位请求更高数量的 STL 三角面,并使用更接近实体的材质透明度。 +- 页面底部 `MODEL PATH` 状态显示当前档位为 `实体`。 + +## 影响范围 + +- `WebSite/src/components/ProjectLibrary.tsx` + - `SolidityLevel` 类型。 + - `solidityOptions` 配置。 + - STL 材质透明度逻辑。 +- `WebSite/server.ts` + - STL preview 的最大抽样上限。 + +## 风险点 + +- 实体档位会请求更多三角面,浏览器渲染压力更高。 +- 若所有 STL 全量三角面过大,接口响应和前端渲染可能变慢。 +- 半透明构件在实体档位可能仍呈现一定透视感,需要统一提升最低不透明度。 + +## 待确认问题 + +- 本次用户已明确免二次确认,直接执行。 + +## 人工审核状态 + +- 本次免二次确认。