Refactor viewport state restoration and enhance error handling for volume and stack loading
This commit is contained in:
File diff suppressed because one or more lines are too long
+58
-69
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user