Refactor viewport state restoration and enhance error handling for volume and stack loading

This commit is contained in:
Ross
2026-03-16 14:08:40 +00:00
parent 8faa7e9be4
commit 52f4be1a96
2 changed files with 154 additions and 69 deletions
File diff suppressed because one or more lines are too long
+58 -69
View File
@@ -2412,9 +2412,9 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
return;
}
// Now safe to restore state for ALL viewports
// First pass: set stacks/volumes and stack positions so viewports have image resources
for (let i = 0; i < numActive; i++) {
console.log("Restoring state for viewport", i);
console.log("Setting stack/volume for viewport", i);
const viewport = renderingEngine.getViewport(`CT_${i}`);
if (!viewport) continue;
const stackIdx = state.stackIdx?.[i];
@@ -2425,86 +2425,75 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
console.log("mode", state.modes?.[i]);
if (state.modes?.[i] === "volume") {
// --- Restore volume ---
console.log("Restoring volume for viewport", i, importedImageIds);
const volumeId = `myVolumeId_${i}`;
const volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: importedImageIds });
volume.load();
viewport.setVolumes([{ volumeId }]);
// --- Restore orientation if present ---
if (state.orientations && state.orientations[i] && typeof viewport.setOrientation === "function") {
try {
viewport.setOrientation(state.orientations[i]);
viewport.render();
} catch (e) {
console.warn("Failed to restore orientation for viewport", i, e);
}
}
if (
state.volumeSliceIndices
) {
console.log("Restoring volume slice index for viewport", i, state.volumeSliceIndices[i]);
setTimeout(() => {
csUtilities.jumpToSlice(viewport.element, { imageIndex: state.volumeSliceIndices[i] });
}, 100);
}
viewport.render();
} else {
// --- Restore stack ---
if (viewport.setStack) {
viewport.setStack(importedImageIds);
try {
const volumeId = `myVolumeId_${i}`;
const volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: importedImageIds });
volume.load();
viewport.setVolumes([{ volumeId }]);
viewport.render();
} catch (e) {
console.warn("Failed to create/load volume for viewport", i, e);
}
// Restore stack position
if (
state.stackPos &&
typeof state.stackPos[i] === "number" &&
typeof viewport.setImageIdIndex === "function"
) {
if (csUtilities && csUtilities.jumpToSlice) {
csUtilities.jumpToSlice(viewport.element, { imageIndex: state.stackPos[i] });
} else {
viewport.setImageIdIndex(state.stackPos[i]);
}
// Best-effort orientation restore
if (state.orientations && state.orientations[i] && typeof viewport.setOrientation === "function") {
try { viewport.setOrientation(state.orientations[i]); viewport.render(); } catch (e) { /* ignore */ }
}
if (state.volumeSliceIndices) {
setTimeout(() => { try { csUtilities.jumpToSlice(viewport.element, { imageIndex: state.volumeSliceIndices[i] }); } catch (e) { } }, 100);
}
} else {
try { if (viewport.setStack) { viewport.setStack(importedImageIds); viewport.render(); } } catch (e) { console.warn('Failed to set stack for viewport', i, e); }
if (state.stackPos && typeof state.stackPos[i] === "number" && typeof viewport.setImageIdIndex === "function") {
try { if (csUtilities && csUtilities.jumpToSlice) csUtilities.jumpToSlice(viewport.element, { imageIndex: state.stackPos[i] }); else viewport.setImageIdIndex(state.stackPos[i]); } catch (e) { /* ignore */ }
}
}
}
// Restore properties (defensive colormap check)
if (
state.props &&
state.props[i] &&
typeof viewport.setProperties === "function"
) {
// Wait briefly to allow viewports to initialize GPU/actor resources
const readyDelayMs = 1500;
console.log(`Waiting ${readyDelayMs}ms before applying properties/camera to viewports`);
await new Promise((res) => setTimeout(res, readyDelayMs));
// Second pass: apply renderer-sensitive properties (colormap, VOI) and camera
for (let i = 0; i < numActive; i++) {
console.log("Applying props/camera for viewport", i);
const viewport = renderingEngine.getViewport(`CT_${i}`);
if (!viewport) continue;
const stackIdx = state.stackIdx?.[i];
const stackObj = state.stacks?.[stackIdx];
const importedImageIds = stackObj?.i || [];
if (!importedImageIds.length) continue;
// Restore properties with sanitization + retry
if (state.props && state.props[i] && typeof viewport.setProperties === "function") {
console.log("Restoring properties for viewport", i, state.props[i]);
const propsToRestore = { ...state.props[i] };
if (
propsToRestore.colormap !== undefined &&
propsToRestore.colormap !== null &&
typeof propsToRestore.colormap !== "string" &&
(typeof propsToRestore.colormap !== "object" || !("name" in propsToRestore.colormap))
) {
delete propsToRestore.colormap;
if (propsToRestore.colormap !== undefined && propsToRestore.colormap !== null) {
const cm = propsToRestore.colormap;
if (typeof cm === 'string') {
// keep
} else if (typeof cm === 'object') {
const safe: any = {};
if (typeof cm.name === 'string') safe.name = cm.name;
if (cm.opacity !== undefined && typeof cm.opacity === 'number') safe.opacity = cm.opacity;
if (Object.keys(safe).length === 0) delete propsToRestore.colormap; else propsToRestore.colormap = safe;
} else {
delete propsToRestore.colormap;
}
}
setTimeout(() => {
try {
viewport.setProperties(propsToRestore);
viewport.render();
}, 100);
} else {
console.warn("No properties to restore for viewport", i);
} catch (err) {
console.warn('viewport.setProperties failed, retrying without colormap', err);
try { const noColormap = { ...propsToRestore }; delete noColormap.colormap; viewport.setProperties(noColormap); viewport.render(); } catch (err2) { console.error('Retrying viewport.setProperties without colormap also failed', err2); }
}
}
// Restore camera
if (
state.cam &&
state.cam[i] &&
typeof viewport.setCamera === "function"
) {
console.log("Restoring camera for viewport", i, state.cam[i]);
viewport.setCamera(state.cam[i]);
viewport.render();
// Restore camera (best-effort)
if (state.cam && state.cam[i] && typeof viewport.setCamera === 'function') {
try { viewport.setCamera(state.cam[i]); viewport.render(); } catch (e) { /* ignore */ }
}
// --- Restore slice index if present ---
}
restoringStateRef.current = false;
}