Skip to content

Commit abc6380

Browse files
authored
fix(planar): map dynamic-volume flattened indices to their group-local slice (#2801)
getVolumeImageIdIndexWorldPoint assumed a one-to-one imageId to k-slice mapping, but StreamingDynamicImageVolume flattens its imageIds across all dimension groups while dimensions[2] is the slice count of a single group. A referenced image or explicitly carried initial slice from any later dimension group therefore had a flattened index of at least dimensions[2], and the clamp sent every such image to the final slice. Convert the flattened index through the volume's own flatImageIdIndexToImageIdIndex mapping (duck-typed; static volumes have no such method and pass through unchanged) before resolving the IJK world center.
1 parent 6d2d80b commit abc6380

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

packages/core/src/RenderingEngine/GenericViewport/Planar/planarSliceBasis.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,21 @@ export function getVolumeImageIdIndexWorldPoint(
344344
}
345345

346346
const dimensions = imageData.getDimensions();
347-
const k = Math.min(Math.max(0, imageIdIndex), dimensions[2] - 1);
347+
// Dynamic (4D) volumes flatten their imageIds across dimension groups while
348+
// dimensions[2] is the slice count of a SINGLE group, so an image from any
349+
// later group has a flattened index >= dimensions[2] — clamping that raw
350+
// index would pin every such image to the final slice. Map it to its
351+
// group-local slice first (the k axis repeats per group).
352+
const flatToGroupLocal = (
353+
imageVolume as {
354+
flatImageIdIndexToImageIdIndex?: (flatImageIdIndex: number) => number;
355+
}
356+
).flatImageIdIndexToImageIdIndex;
357+
const groupLocalIndex =
358+
typeof flatToGroupLocal === 'function'
359+
? flatToGroupLocal.call(imageVolume, Math.max(0, imageIdIndex))
360+
: imageIdIndex;
361+
const k = Math.min(Math.max(0, groupLocalIndex), dimensions[2] - 1);
348362

349363
return imageData.indexToWorld([
350364
(dimensions[0] - 1) / 2,

packages/core/test/planarSliceBasis.jest.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,28 @@ describe('resolvePlanarVolumeImageIdIndex', () => {
851851
});
852852

853853
describe('getVolumeImageIdIndexWorldPoint', () => {
854+
it('maps a flattened dynamic-volume index to its group-local slice', () => {
855+
// A 4D volume flattens its imageIds across dimension groups (here 3
856+
// groups of 6 slices) while dimensions[2] stays the per-group slice
857+
// count, and exposes the flat -> group-local mapping.
858+
const volume = createOrthonormalVolume();
859+
860+
volume.flatImageIdIndexToImageIdIndex = (flatImageIdIndex) =>
861+
flatImageIdIndex % 6;
862+
863+
// Flattened index 14 = group 3, local slice 2 — NOT the last slice (5)
864+
// that a raw clamp against dimensions[2] - 1 would produce.
865+
expectPoint3Close(
866+
getVolumeImageIdIndexWorldPoint(volume, 14),
867+
[102.25, 203.5, 304]
868+
);
869+
// Indices inside the first group pass through the mapping unchanged.
870+
expectPoint3Close(
871+
getVolumeImageIdIndexWorldPoint(volume, 2),
872+
[102.25, 203.5, 304]
873+
);
874+
});
875+
854876
it('returns the exact IJK slice center for an imageId-list index', () => {
855877
const volume = createOrthonormalVolume();
856878

0 commit comments

Comments
 (0)