2026-05-24-20-06-08 修正可见构件映射错位

This commit is contained in:
2026-05-24 20:17:40 +08:00
parent 2510937128
commit 3d732ec898
6 changed files with 213 additions and 3 deletions

View File

@@ -2110,6 +2110,8 @@ function drawVoxelOverlayLayer(
preview: DicomPreview,
files: string[],
previews: Record<string, ModelPreviewPayload>,
metricFiles: string[],
metricPreviews: Record<string, ModelPreviewPayload>,
moduleStyles: Record<string, ModuleStyle>,
modelPose: ModelPose,
slice: number,
@@ -2124,7 +2126,8 @@ function drawVoxelOverlayLayer(
}
context.clearRect(0, 0, fovCanvas.width, fovCanvas.height);
const metrics = getModelSceneMetrics(files, previews, preview, totalSlices);
const metrics = getModelSceneMetrics(metricFiles, metricPreviews, preview, totalSlices)
?? getModelSceneMetrics(files, previews, preview, totalSlices);
if (!metrics) {
return { activeModules: 0, filledPixels: 0, segmentCount: 0, modules: [] };
}
@@ -2243,6 +2246,8 @@ export function VoxelizationMappingView({
const mappingViewportRef = useRef<HTMLDivElement | null>(null);
const [dicomPreview, setDicomPreview] = useState<DicomPreview | null>(null);
const [modelPreviews, setModelPreviews] = useState<Record<string, ModelPreviewPayload>>({});
const [metricPreviews, setMetricPreviews] = useState<Record<string, ModelPreviewPayload>>({});
const [metricPreviewsLoaded, setMetricPreviewsLoaded] = useState(false);
const [dicomStatus, setDicomStatus] = useState('等待 DICOM 切片');
const [overlayStatus, setOverlayStatus] = useState('等待 STL 映射');
const [overlayStats, setOverlayStats] = useState<OverlayStats>({ activeModules: 0, filledPixels: 0, segmentCount: 0, modules: [] });
@@ -2259,9 +2264,11 @@ export function VoxelizationMappingView({
const maxSlice = Math.max(totalSlices - 1, 0);
const safeSlice = clamp(slice, 0, maxSlice);
const stlFiles = project?.stlFiles ?? [];
const stlFileSignature = stlFiles.join('|');
const visibleStlFiles = stlFiles.filter((fileName) => moduleStyles[fileName]?.visible !== false);
const visibleStlFileSignature = visibleStlFiles.join('|');
const visibleModuleCount = visibleStlFiles.length;
const metricPreviewsReady = !stlFiles.length || metricPreviewsLoaded;
const isLibraryVariant = variant === 'library';
const activeOverlayPlacement = overlayPlacement ?? (isLibraryVariant ? 'side' : 'bottom');
@@ -2295,6 +2302,36 @@ export function VoxelizationMappingView({
};
}, [project?.id, project?.dicomCount, safeSlice, displayMode]);
useEffect(() => {
if (!project || !stlFiles.length) {
setMetricPreviews({});
setMetricPreviewsLoaded(true);
return;
}
let disposed = false;
setMetricPreviews({});
setMetricPreviewsLoaded(false);
Promise.allSettled(stlFiles.map((fileName) => (
getCachedModelPreview(project.id, fileName, 1000)
.then((payload) => ({ fileName, payload }))
))).then((results) => {
if (disposed) return;
const nextPreviews: Record<string, ModelPreviewPayload> = {};
results.forEach((result) => {
if (result.status === 'fulfilled') {
nextPreviews[result.value.fileName] = result.value.payload;
}
});
setMetricPreviews(nextPreviews);
setMetricPreviewsLoaded(true);
});
return () => {
disposed = true;
};
}, [project?.id, stlFileSignature]);
useEffect(() => {
if (!project || !visibleStlFiles.length) {
setModelPreviews({});
@@ -2346,7 +2383,7 @@ export function VoxelizationMappingView({
return () => {
disposed = true;
};
}, [project?.id, stlFiles.length, visibleStlFileSignature, detailLimit]);
}, [project?.id, stlFileSignature, visibleStlFileSignature, detailLimit]);
useEffect(() => {
const canvas = baseCanvasRef.current;
@@ -2361,12 +2398,21 @@ export function VoxelizationMappingView({
if (!canvas || !dicomPreview) {
return;
}
if (!metricPreviewsReady) {
const context = canvas.getContext('2d');
context?.clearRect(0, 0, canvas.width, canvas.height);
setOverlayStats({ activeModules: 0, filledPixels: 0, segmentCount: 0, modules: [] });
setOverlayStatus('正在计算全局模型边界...');
return;
}
const frame = window.requestAnimationFrame(() => {
const stats = drawVoxelOverlayLayer(
canvas,
dicomPreview,
visibleStlFiles,
modelPreviews,
stlFiles,
metricPreviews,
moduleStyles,
modelPose,
safeSlice,
@@ -2378,8 +2424,11 @@ export function VoxelizationMappingView({
return () => window.cancelAnimationFrame(frame);
}, [
dicomPreview,
stlFileSignature,
visibleStlFileSignature,
modelPreviews,
metricPreviews,
metricPreviewsReady,
JSON.stringify(moduleStyles),
modelPose.rotateX,
modelPose.rotateY,