2026-05-07-17-05-43 增加3D实体显示档位

This commit is contained in:
2026-05-07 17:09:18 +08:00
parent bbc7d215e9
commit 97f5c78907
6 changed files with 188 additions and 8 deletions

View File

@@ -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;

View File

@@ -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<string, ModuleStyle>;
detailLimit: number;
solidMode: boolean;
pose: ModelPose;
onPoseChange: React.Dispatch<React.SetStateAction<ModelPose>>;
}) {
@@ -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 (
<div className="h-full w-full relative cursor-grab active:cursor-grabbing">
@@ -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<string, ModuleStyle> = {};
@@ -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}
/>

View File

@@ -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`
- 已在实体档位提高材质不透明度下限,增强实体面片视觉。

View File

@@ -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`,确认实体档位可返回完整三角面。

View File

@@ -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、材质表现和浏览器性能边界。

View File

@@ -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 全量三角面过大,接口响应和前端渲染可能变慢。
- 半透明构件在实体档位可能仍呈现一定透视感,需要统一提升最低不透明度。
## 待确认问题
- 本次用户已明确免二次确认,直接执行。
## 人工审核状态
- 本次免二次确认。