Skip to content
Merged
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
2,663 changes: 210 additions & 2,453 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@itk-wasm/dicom": "^7.6.4",
"@itk-wasm/image-io": "1.6.1",
"@itk-wasm/morphological-contour-interpolation": "2.0.0",
"@kitware/vtk.js": "^32.12.1",
"@kitware/vtk.js": "^36.2.0",
"@netlify/edge-functions": "^3.0.2",
"@rollup/plugin-replace": "^6.0.3",
"@sentry/vite-plugin": "^4.6.1",
Expand All @@ -62,7 +62,7 @@
"@wdio/static-server-service": "^9.20.0",
"@wdio/visual-service": "9.0.2",
"comlink": "^4.4.2",
"concurrently": "^9.2.1",
"concurrently": "^10.0.3",
"core-js": "3.47.0",
"cors": "^2.8.5",
"cross-env": "^10.1.0",
Expand Down Expand Up @@ -92,7 +92,7 @@
"ts-node": "^10.9.2",
"typescript": "~5.9.3",
"typescript-eslint": "^8.32.1",
"vite": "^7.2.4",
"vite": "^7.3.5",
"vite-plugin-html": "^3.2.2",
"vite-plugin-static-copy": "^3.1.4",
"vite-plugin-vuetify": "^2.1.2",
Expand Down
256 changes: 128 additions & 128 deletions server/poetry.lock

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions src/components/vtk/VtkBaseVolumeRepresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ useEventListener(

watch(updatedExtents, (current, old) => {
const startOffset = old.length;
rep.mapper.setUpdatedExtents([
...rep.mapper.getUpdatedExtents(),
rep.property.setUpdatedExtents([
...rep.property.getUpdatedExtents(),
...current.slice(startOffset),
]);
view.requestRender();
Expand Down Expand Up @@ -149,22 +149,23 @@ watchEffect(() => {

setCinematicVolumeScatter({
enabled: enabled && useVolumetricScatteringBlending,
mapper,
property,
blending: volumetricScatteringBlending,
});

setCinematicVolumeSampling({
enabled,
image: img,
mapper,
property,
quality: volumeQuality,
});

setCinematicLocalAmbientOcclusion({
enabled: enabled && useLocalAmbientOcclusion,
kernelRadius: laoKernelRadius,
kernelSize: laoKernelSize,
mapper,
property,
});

view.requestRender();
Expand Down
50 changes: 50 additions & 0 deletions src/composables/__tests__/useAutoFitState.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { effectScope, type EffectScope } from 'vue';
import vtkCamera from '@kitware/vtk.js/Rendering/Core/Camera';
import { useAutoFitState } from '@/src/composables/useAutoFitState';

describe('useAutoFitState', () => {
// Each test runs the composable inside a disposable effect scope so the
// onScopeDispose/watch machinery in onVTKEvent has a scope to attach to,
// mirroring component setup.
let scope: EffectScope;
beforeEach(() => {
scope = effectScope(true);
});
afterEach(() => scope.stop());
const run = <T>(fn: () => T) => scope.run(fn)!;

it('starts with auto-fit enabled', () => {
const camera = vtkCamera.newInstance();
const autoFit = run(() => useAutoFitState(camera));
expect(autoFit.autoFit.value).toBe(true);
});

it('keeps auto-fit enabled through programmatic camera changes outside a pointer interaction', () => {
const camera = vtkCamera.newInstance();
const autoFit = run(() => useAutoFitState(camera));

// Initial programmatic fit, as resetCamera() does on image load.
autoFit.withPaused(() => {
camera.setParallelScale(5);
});
expect(autoFit.autoFit.value).toBe(true);

// A later non-user-driven camera change, e.g. resetCameraClippingRange()
// fired from useVtkView.setSize() on a layout/resize. This must NOT
// disable auto-fit, otherwise the next resize will not refit the slice.
camera.modified();
expect(autoFit.autoFit.value).toBe(true);
});

it('disables auto-fit when the user manipulates the camera during a pointer interaction', () => {
const camera = vtkCamera.newInstance();
const autoFit = run(() => useAutoFitState(camera));

autoFit.resume(); // pointerdown
camera.setParallelScale(42); // user zoom/pan modifies the camera
autoFit.pause(); // pointerup

expect(autoFit.autoFit.value).toBe(false);
});
});
3 changes: 2 additions & 1 deletion src/composables/onPausableVTKEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ export function onPausableVTKEvent<T extends vtkObject, K extends keyof T>(
};

const withPaused = (fn: () => void) => {
const wasPaused = paused;
pause();
try {
fn();
} finally {
resume();
paused = wasPaused;
}
};

Expand Down
28 changes: 15 additions & 13 deletions src/utils/volumeProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@ export function setCinematicLighting({
export interface SetCinematicVolumeSamplingParameters {
enabled: boolean;
mapper: vtkVolumeMapper;
property: vtkVolumeProperty;
quality: number;
image: vtkImageData;
}

export function setCinematicVolumeSampling({
enabled,
mapper,
property,
quality,
image,
}: SetCinematicVolumeSamplingParameters) {
Expand All @@ -103,8 +105,8 @@ export function setCinematicVolumeSampling({
mapper.setMaximumSamplesPerRay(samplesPerRay);
mapper.setSampleDistance(sampleDistance);
// Adjust the global illumination reach by volume quality slider
mapper.setGlobalIlluminationReach(enabled ? 0.25 * quality : 0);
mapper.setComputeNormalFromOpacity(!enabled && quality > 2);
property.setGlobalIlluminationReach(enabled ? 0.25 * quality : 0);
property.setComputeNormalFromOpacity(!enabled && quality > 2);
}

export interface SetCinematicVolumeShadingParameters {
Expand Down Expand Up @@ -151,38 +153,38 @@ export function setCinematicVolumeShading({

export interface SetCinematicVolumeScatterParameters {
enabled: boolean;
mapper: vtkVolumeMapper;
property: vtkVolumeProperty;
blending: number;
}

export function setCinematicVolumeScatter({
enabled,
mapper,
property,
blending,
}: SetCinematicVolumeScatterParameters) {
mapper.setVolumetricScatteringBlending(enabled ? blending : 0);
property.setVolumetricScatteringBlending(enabled ? blending : 0);
}

export interface SetCinematicLocalAmbientOcclusionParameters {
enabled: boolean;
mapper: vtkVolumeMapper;
property: vtkVolumeProperty;
kernelSize: number;
kernelRadius: number;
}

export function setCinematicLocalAmbientOcclusion({
enabled,
mapper,
property,
kernelSize,
kernelRadius,
}: SetCinematicLocalAmbientOcclusionParameters) {
if (enabled) {
mapper.setLocalAmbientOcclusion(true);
mapper.setLAOKernelSize(kernelSize);
mapper.setLAOKernelRadius(kernelRadius);
property.setLocalAmbientOcclusion(true);
property.setLAOKernelSize(kernelSize);
property.setLAOKernelRadius(kernelRadius);
} else {
mapper.setLocalAmbientOcclusion(false);
mapper.setLAOKernelSize(0);
mapper.setLAOKernelRadius(0);
property.setLocalAmbientOcclusion(false);
property.setLAOKernelSize(0);
property.setLAOKernelRadius(0);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions tests/specs/state-manifest.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { FIXTURES, WINDOW_SIZE } from '../../wdio.shared.conf';
import { FIXTURES, applyTestViewport } from '../../wdio.shared.conf';
import { volViewPage } from '../pageobjects/volview.page';
import { openVolViewPage, writeManifestToZip } from './utils';

Expand All @@ -16,7 +16,7 @@ describe('State file manifest.json code', () => {

it('loads 5.0.1 manifest with axial layer layout', async () => {
await browser.reloadSession();
await browser.setWindowSize(...WINDOW_SIZE);
await applyTestViewport(browser);
const manifestPath = path.join(FIXTURES, 'layer-axial.5-0-1.volview.json');
const fileName = 'temp-layer-axial.volview.zip';
await writeManifestToZip(manifestPath, fileName);
Expand Down
2 changes: 1 addition & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export default defineConfig({
if (id.includes('@kitware/vtk.js')) {
if (id.includes('ColorMaps.json.js')) {
// We don't use the built-in colormaps
return 'export const v = []';
return 'export default [];';
}

// We don't use these classes
Expand Down
15 changes: 11 additions & 4 deletions wdio.shared.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ const TEST_DATASETS = [
},
];

export const WINDOW_SIZE = [1200, 800] as const;
// Fixed capture viewport (Playwright's default).
export const CONTENT_VIEWPORT = { width: 1280, height: 720 } as const;

// Pin the content viewport so capture geometry is stable across Chrome versions
// and OSes (independent of the OS window). Enables one shared baseline.
export const applyTestViewport = (browser: any) =>
browser.setViewport({ ...CONTENT_VIEWPORT, devicePixelRatio: 1 });

export const TEST_PORT = 4567;
// for slow connections try:
// DOWNLOAD_TIMEOUT=60000 && npm run test:e2e:dev
Expand Down Expand Up @@ -89,8 +96,8 @@ export const config: Options.Testrunner = {
'visual',
{
baselineFolder: path.resolve(ROOT, 'tests/baseline/'),
formatImageName:
'{tag}-{browserName}-{platformName}-{width}x{height}-{dpr}',
// Pinned geometry, so no {platformName}/{width}x{height}; one shared baseline.
formatImageName: '{tag}-{browserName}-{dpr}',
screenshotPath: TEMP_DIR,
autoSaveBaseline: true,
},
Expand Down Expand Up @@ -161,7 +168,7 @@ export const config: Options.Testrunner = {
_specs: string[],
browser: any
) {
await browser.setWindowSize(...WINDOW_SIZE);
await applyTestViewport(browser);

// Subscribe to browser console logs and output them directly
await browser.sessionSubscribe({ events: ['log.entryAdded'] });
Expand Down
Loading