Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/sliceview/base.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*/

import { describe, it, expect } from "vitest";
import type { ProjectionParameters } from "#src/projection_parameters.js";
import type { TransformedSource } from "#src/sliceview/base.js";
import {
estimateSliceAreaPerChunk,
forEachVisibleVolumetricChunk,
getNearIsotropicBlockSize,
} from "#src/sliceview/base.js";
import { ChunkLayout } from "#src/sliceview/chunk_layout.js";
Expand Down Expand Up @@ -200,3 +203,61 @@ describe("estimateSliceAreaPerChunk", () => {
}
});
});

describe("forEachVisibleVolumetricChunk", () => {
it("does not clamp zeroed display-dim positions to the chunk origin", () => {
// `xy` slice view of a rank-3 volume whose origin is *not* at (0, 0, 0)
const tsource = {
source: {
spec: {
rank: 3,
chunkDataSize: Uint32Array.of(1024, 1024, 1),
// Stack origin at voxel (64056, 33042, 20) -> nonzero chunk bounds.
lowerChunkBound: Float32Array.of(62, 32, 20),
upperChunkBound: Float32Array.of(64, 34, 21),
},
},
layerRank: 3,
// The following data is prepared as would be done by
// `getVolumetricTransformedSources`: display-dim rows (x, y) are zeroed and
// z maps identically from global z with no translation.
fixedLayerToChunkTransform: Float32Array.of(
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
),
// Display dims (x, y) get infinite clip bounds; only z is finite.
nonDisplayLowerClipBound: Float32Array.of(-Infinity, -Infinity, 20),
nonDisplayUpperClipBound: Float32Array.of(Infinity, Infinity, 21),
chunkLayout: new ChunkLayout(vec3.fromValues(1, 1, 1), mat4.create(), 3),
lowerChunkDisplayBound: vec3.fromValues(62, 32, 20),
upperChunkDisplayBound: vec3.fromValues(64, 34, 21),
chunkDisplayDimensionIndices: [0, 1, 2],
curPositionInChunks: new Float32Array(3),
// Sentinel so an early return (source excluded) can't masquerade as a pass.
fixedPositionWithinChunk: Uint32Array.of(999, 999, 999),
} as unknown as TransformedSource;

forEachVisibleVolumetricChunk(
{
// x/y are irrelevant (transform rows zeroed); z is at slice 20.
globalPosition: Float32Array.of(70000, 40000, 20),
viewProjectionMat: mat4.create(),
} as unknown as ProjectionParameters,
new Float32Array(0),
tsource,
() => {},
);

expect(Array.from(tsource.fixedPositionWithinChunk)).toEqual([0, 0, 0]);
});
});
18 changes: 17 additions & 1 deletion src/sliceview/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ function updateFixedCurPositionInChunks<
globalPosition: Float32Array,
localPosition: Float32Array,
): boolean {
const { curPositionInChunks, fixedPositionWithinChunk } = tsource;
const {
curPositionInChunks,
fixedPositionWithinChunk,
chunkDisplayDimensionIndices,
} = tsource;
const { nonDisplayLowerClipBound, nonDisplayUpperClipBound } = tsource;
const { rank, chunkDataSize, lowerChunkBound, upperChunkBound } =
tsource.source.spec;
Expand Down Expand Up @@ -218,6 +222,18 @@ function updateFixedCurPositionInChunks<
}
return false;
}
if (chunkDisplayDimensionIndices.includes(chunkDim)) {
// This function computes only the *fixed* (non-display) part of the
// position. The rows of `fixedLayerToChunkTransform` corresponding to
// display dimensions are zeroed, so `x` is a placeholder 0 rather than a
// real coordinate; the actual chunk index is filled in by the caller's
// iteration over the display subspace. Clamping the placeholder to
// `lowerChunkBound` would, for a source with a non-zero
// `spec.lowerVoxelBound`, force `chunk` past `x` and leave a negative
// `fixedPositionWithinChunk`.
fixedPositionWithinChunk[chunkDim] = 0;
continue;
}
const chunkSize = chunkDataSize[chunkDim];
// Given that clip bounds are already tested above, clamp chunk index to its
// bounds, to ensure floating-point imprecision does not result in an
Expand Down
Loading