diff --git a/packages/core/src/RenderingEngine/GenericViewport/Planar/planarSliceBasis.ts b/packages/core/src/RenderingEngine/GenericViewport/Planar/planarSliceBasis.ts index 9f078a242a..dcf659d479 100644 --- a/packages/core/src/RenderingEngine/GenericViewport/Planar/planarSliceBasis.ts +++ b/packages/core/src/RenderingEngine/GenericViewport/Planar/planarSliceBasis.ts @@ -344,7 +344,21 @@ export function getVolumeImageIdIndexWorldPoint( } const dimensions = imageData.getDimensions(); - const k = Math.min(Math.max(0, imageIdIndex), dimensions[2] - 1); + // Dynamic (4D) volumes flatten their imageIds across dimension groups while + // dimensions[2] is the slice count of a SINGLE group, so an image from any + // later group has a flattened index >= dimensions[2] — clamping that raw + // index would pin every such image to the final slice. Map it to its + // group-local slice first (the k axis repeats per group). + const flatToGroupLocal = ( + imageVolume as { + flatImageIdIndexToImageIdIndex?: (flatImageIdIndex: number) => number; + } + ).flatImageIdIndexToImageIdIndex; + const groupLocalIndex = + typeof flatToGroupLocal === 'function' + ? flatToGroupLocal.call(imageVolume, Math.max(0, imageIdIndex)) + : imageIdIndex; + const k = Math.min(Math.max(0, groupLocalIndex), dimensions[2] - 1); return imageData.indexToWorld([ (dimensions[0] - 1) / 2, diff --git a/packages/core/test/planarSliceBasis.jest.js b/packages/core/test/planarSliceBasis.jest.js index a03544dd57..85cec0e873 100644 --- a/packages/core/test/planarSliceBasis.jest.js +++ b/packages/core/test/planarSliceBasis.jest.js @@ -851,6 +851,28 @@ describe('resolvePlanarVolumeImageIdIndex', () => { }); describe('getVolumeImageIdIndexWorldPoint', () => { + it('maps a flattened dynamic-volume index to its group-local slice', () => { + // A 4D volume flattens its imageIds across dimension groups (here 3 + // groups of 6 slices) while dimensions[2] stays the per-group slice + // count, and exposes the flat -> group-local mapping. + const volume = createOrthonormalVolume(); + + volume.flatImageIdIndexToImageIdIndex = (flatImageIdIndex) => + flatImageIdIndex % 6; + + // Flattened index 14 = group 3, local slice 2 — NOT the last slice (5) + // that a raw clamp against dimensions[2] - 1 would produce. + expectPoint3Close( + getVolumeImageIdIndexWorldPoint(volume, 14), + [102.25, 203.5, 304] + ); + // Indices inside the first group pass through the mapping unchanged. + expectPoint3Close( + getVolumeImageIdIndexWorldPoint(volume, 2), + [102.25, 203.5, 304] + ); + }); + it('returns the exact IJK slice center for an imageId-list index', () => { const volume = createOrthonormalVolume();