diff --git a/package-lock.json b/package-lock.json index a57724420..22978b6ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,12 @@ "@aws-sdk/client-s3": "^3.435.0", "@itk-wasm/dicom": "6.0.1", "@itk-wasm/image-io": "1.1.1", - "@kitware/vtk.js": "^29.0.0", + "@kitware/vtk.js": "30.9.0", "@netlify/edge-functions": "^2.0.0", "@sentry/vue": "^7.54.0", "@velipso/polybool": "^1.1.0", - "@vueuse/core": "^10.7.0", + "@vueuse/core": "10.7.2", + "comlink": "^4.4.1", "core-js": "3.22.5", "deep-equal": "^2.0.5", "dicomweb-client-typed": "^0.8.6", @@ -32,7 +33,7 @@ "socket.io-parser": "^4.2.4", "vue": "^3.3.4", "vue-toastification": "^2.0.0-rc.5", - "vuetify": "^3.1.14", + "vuetify": "^3.4.0", "zod": "^3.22.3" }, "devDependencies": { @@ -3583,9 +3584,9 @@ } }, "node_modules/@kitware/vtk.js": { - "version": "29.11.2", - "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-29.11.2.tgz", - "integrity": "sha512-SMYz7E5QlnMxg/iuNLYlae993EFG+eHjcQkGwWpGtPS2ANSnJdGiS0tCdc1LJz6gQ5LK01yZzObIHW472BybLQ==", + "version": "30.9.0", + "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-30.9.0.tgz", + "integrity": "sha512-BrQDp/mMT7d3xiTda7cH4hqQQuNQawNk2rSRwsyVxaAU7SV1brLGlKCS7uqs0Jk1qRcL0L7BTcJrXMPj5HhRyQ==", "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", @@ -3611,7 +3612,7 @@ "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", - "wslink": "^1.1.0" + "wslink": ">=1.1.0 || ^2.0.0" } }, "node_modules/@ljharb/through": { @@ -6149,14 +6150,25 @@ } }, "node_modules/@vueuse/core": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", - "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.11.0", - "@vueuse/shared": "10.11.0", - "vue-demi": ">=0.14.8" + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", + "vue-demi": ">=0.14.6" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/@vueuse/shared": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.2.tgz", + "integrity": "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==", + "dependencies": { + "vue-demi": ">=0.14.6" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -6253,6 +6265,30 @@ } } }, + "node_modules/@vueuse/integrations/node_modules/@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations/node_modules/@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@vueuse/integrations/node_modules/vue-demi": { "version": "0.14.8", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", @@ -6280,9 +6316,9 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", - "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", "funding": { "url": "https://github.com/sponsors/antfu" } @@ -6291,6 +6327,7 @@ "version": "10.11.0", "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz", "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==", + "dev": true, "dependencies": { "vue-demi": ">=0.14.8" }, @@ -6302,6 +6339,7 @@ "version": "0.14.8", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -21500,6 +21538,56 @@ "integrity": "sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==", "dev": true }, + "node_modules/vitepress/node_modules/@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/vitepress/node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vitepress/node_modules/@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/vitepress/node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -21911,9 +21999,9 @@ } }, "node_modules/vuetify": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.1.14.tgz", - "integrity": "sha512-KpJH7CDyDwLjfLUkKgmijZ7WMBscuGTGKeO5pomerEWQiZ9X9bjHJTXdycqFX60QSl3AkrJeSIWW/l8nkxoslw==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.6.9.tgz", + "integrity": "sha512-LVIZL0OCwzg3Aw2F8nT1rK/renpTPOcDEPe5E002Vj+VjXwsCNqwwxNteGfrcNlp0vgIJ8WusMvaxp69UJ3NJg==", "engines": { "node": "^12.20 || >=14.13" }, @@ -21922,12 +22010,16 @@ "url": "https://github.com/sponsors/johnleider" }, "peerDependencies": { - "vite-plugin-vuetify": "^1.0.0-alpha.12", - "vue": "^3.2.0", + "typescript": ">=4.7", + "vite-plugin-vuetify": ">=1.0.0", + "vue": "^3.3.0", "vue-i18n": "^9.0.0", - "webpack-plugin-vuetify": "^2.0.0-alpha.11" + "webpack-plugin-vuetify": ">=2.0.0" }, "peerDependenciesMeta": { + "typescript": { + "optional": true + }, "vite-plugin-vuetify": { "optional": true }, @@ -26096,9 +26188,9 @@ } }, "@kitware/vtk.js": { - "version": "29.11.2", - "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-29.11.2.tgz", - "integrity": "sha512-SMYz7E5QlnMxg/iuNLYlae993EFG+eHjcQkGwWpGtPS2ANSnJdGiS0tCdc1LJz6gQ5LK01yZzObIHW472BybLQ==", + "version": "30.9.0", + "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-30.9.0.tgz", + "integrity": "sha512-BrQDp/mMT7d3xiTda7cH4hqQQuNQawNk2rSRwsyVxaAU7SV1brLGlKCS7uqs0Jk1qRcL0L7BTcJrXMPj5HhRyQ==", "requires": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", @@ -28140,16 +28232,24 @@ } }, "@vueuse/core": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", - "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", "requires": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.11.0", - "@vueuse/shared": "10.11.0", - "vue-demi": ">=0.14.8" + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", + "vue-demi": ">=0.14.6" }, "dependencies": { + "@vueuse/shared": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.2.tgz", + "integrity": "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==", + "requires": { + "vue-demi": ">=0.14.6" + } + }, "vue-demi": { "version": "0.14.8", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", @@ -28169,6 +28269,24 @@ "vue-demi": ">=0.14.8" }, "dependencies": { + "@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "requires": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + } + }, + "@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", + "dev": true + }, "vue-demi": { "version": "0.14.8", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", @@ -28179,14 +28297,15 @@ } }, "@vueuse/metadata": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", - "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==" + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==" }, "@vueuse/shared": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz", "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==", + "dev": true, "requires": { "vue-demi": ">=0.14.8" }, @@ -28195,6 +28314,7 @@ "version": "0.14.8", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, "requires": {} } } @@ -39523,6 +39643,33 @@ "integrity": "sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==", "dev": true }, + "@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "requires": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "requires": {} + } + } + }, + "@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", + "dev": true + }, "entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -39768,9 +39915,9 @@ } }, "vuetify": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.1.14.tgz", - "integrity": "sha512-KpJH7CDyDwLjfLUkKgmijZ7WMBscuGTGKeO5pomerEWQiZ9X9bjHJTXdycqFX60QSl3AkrJeSIWW/l8nkxoslw==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.6.9.tgz", + "integrity": "sha512-LVIZL0OCwzg3Aw2F8nT1rK/renpTPOcDEPe5E002Vj+VjXwsCNqwwxNteGfrcNlp0vgIJ8WusMvaxp69UJ3NJg==", "requires": {} }, "w3c-xmlserializer": { diff --git a/package.json b/package.json index 597e1df49..b58b49382 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,12 @@ "@aws-sdk/client-s3": "^3.435.0", "@itk-wasm/dicom": "6.0.1", "@itk-wasm/image-io": "1.1.1", - "@kitware/vtk.js": "^29.0.0", + "@kitware/vtk.js": "30.9.0", "@netlify/edge-functions": "^2.0.0", "@sentry/vue": "^7.54.0", "@velipso/polybool": "^1.1.0", - "@vueuse/core": "^10.7.0", + "@vueuse/core": "10.7.2", + "comlink": "^4.4.1", "core-js": "3.22.5", "deep-equal": "^2.0.5", "dicomweb-client-typed": "^0.8.6", @@ -48,7 +49,7 @@ "socket.io-parser": "^4.2.4", "vue": "^3.3.4", "vue-toastification": "^2.0.0-rc.5", - "vuetify": "^3.1.14", + "vuetify": "^3.4.0", "zod": "^3.22.3" }, "devDependencies": { diff --git a/patches/@kitware+vtk.js+30.9.0.patch b/patches/@kitware+vtk.js+30.9.0.patch new file mode 100644 index 000000000..3e7a69d31 --- /dev/null +++ b/patches/@kitware+vtk.js+30.9.0.patch @@ -0,0 +1,548 @@ +diff --git a/node_modules/@kitware/vtk.js/Interaction/Widgets/PiecewiseGaussianWidget.js b/node_modules/@kitware/vtk.js/Interaction/Widgets/PiecewiseGaussianWidget.js +index d317f28..a8c6c71 100644 +--- a/node_modules/@kitware/vtk.js/Interaction/Widgets/PiecewiseGaussianWidget.js ++++ b/node_modules/@kitware/vtk.js/Interaction/Widgets/PiecewiseGaussianWidget.js +@@ -470,16 +470,17 @@ function vtkPiecewiseGaussianWidget(publicAPI, model) { + // information to choose which component you want to extract the histogram + // from. + +- publicAPI.setDataArray = function (array) { ++ publicAPI.setDataArray = function (dataArray) { + let { + numberOfBinToConsiders = 1, + numberOfBinsToSkip = 1, + numberOfComponents = 1, + component = 0 + } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; ++ const array = dataArray.getData(); + model.histogram = null; + model.histogramArray = array; +- model.dataRange = arrayRange(array, component, numberOfComponents); ++ model.dataRange = dataArray.getRange(); + const [min, max] = model.dataRange; + const maxNumberOfWorkers = 4; + let arrayStride = Math.floor(array.length / maxNumberOfWorkers) || 1; +diff --git a/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.d.ts b/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.d.ts +index b749d1e..7164573 100644 +--- a/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.d.ts ++++ b/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.d.ts +@@ -1,5 +1,5 @@ + import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction'; +-import { Bounds, Range } from './../../types'; ++import { Bounds, Range, Extent } from './../../types'; + import vtkAbstractMapper3D, { + IAbstractMapper3DInitialValues, + } from './AbstractMapper3D'; +@@ -281,6 +281,39 @@ export interface vtkVolumeMapper extends vtkAbstractMapper3D { + */ + setLAOKernelRadius(LAOKernelRadius: number): void; + ++ /** ++ * Set kernel size for local ambient occlusion. It specifies the number of rays that are randomly sampled in the hemisphere. ++ * Value is clipped between 1 and 32. ++ * @param LAOKernelSize ++ */ ++ setLAOKernelSize(LAOKernelSize: number): void; ++ ++ /** ++ * Set kernel radius for local ambient occlusion. It specifies the number of samples that are considered on each random ray. ++ * Value must be greater than or equal to 1. ++ * @param LAOKernelRadius ++ */ ++ setLAOKernelRadius(LAOKernelRadius: number): void; ++ ++ /** ++ * Tells the mapper to only update the specified extents. ++ * ++ * If there are zero extents, the mapper updates the entire volume texture. ++ * Otherwise, the mapper will only update the texture by the specified extents ++ * during the next render call. ++ * ++ * This array is cleared after a successful render. ++ * @param extents ++ */ ++ setUpdatedExtents(extents: Extent[]): boolean; ++ ++ /** ++ * Retrieves the updated extents. ++ * ++ * This array is cleared after every successful render. ++ */ ++ getUpdatedExtents(): Extent[]; ++ + /** + * + */ +diff --git a/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.js b/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.js +index 8b37e6c..918609a 100644 +--- a/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.js ++++ b/node_modules/@kitware/vtk.js/Rendering/Core/VolumeMapper.js +@@ -101,7 +101,7 @@ function vtkVolumeMapper(publicAPI, model) { + // ---------------------------------------------------------------------------- + + // TODO: what values to use for averageIPScalarRange to get GLSL to use max / min values like [-Math.inf, Math.inf]? +-const DEFAULT_VALUES = { ++const defaultValues = initialValues => ({ + bounds: [1, -1, 1, -1, 1, -1], + sampleDistance: 1.0, + imageSampleDistance: 1.0, +@@ -124,16 +124,18 @@ const DEFAULT_VALUES = { + // local ambient occlusion + localAmbientOcclusion: false, + LAOKernelSize: 15, +- LAOKernelRadius: 7 +-}; ++ LAOKernelRadius: 7, ++ updatedExtents: [], ++ ...initialValues ++}); + + // ---------------------------------------------------------------------------- + + function extend(publicAPI, model) { + let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; +- Object.assign(model, DEFAULT_VALUES, initialValues); ++ Object.assign(model, defaultValues(initialValues)); + vtkAbstractMapper3D.extend(publicAPI, model, initialValues); +- macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'volumeShadowSamplingDistFactor', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius']); ++ macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'volumeShadowSamplingDistFactor', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius', 'updatedExtents']); + macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2); + macro.event(publicAPI, model, 'lightingActivated'); + +diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.d.ts b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.d.ts +index 6f5c175..0940c84 100644 +--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.d.ts ++++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.d.ts +@@ -1,6 +1,6 @@ + import { Wrap, Filter } from './Texture/Constants'; + import vtkOpenGLRenderWindow from './RenderWindow'; +-import { Nullable } from './../../types'; ++import { Extent, Nullable } from './../../types'; + import { VtkDataTypes } from './../../Common/Core/DataArray'; + import { vtkViewNode } from './../SceneGraph/ViewNode'; + import { vtkObject } from './../../interfaces'; +@@ -256,12 +256,16 @@ export interface vtkOpenGLTexture extends vtkViewNode { + + /** + * Creates a 3D texture from raw data. ++ * ++ * updatedExtents is currently incompatible with webgl1, since there's no extent scaling. ++ * + * @param width The width of the texture. + * @param height The height of the texture. + * @param depth The depth of the texture. + * @param numComps The number of components in the texture. + * @param dataType The data type of the texture. + * @param data The raw data for the texture. ++ * @param updatedExtents Only update the specified extents (default: []) + * @returns {boolean} True if the texture was successfully created, false otherwise. + */ + create3DFromRaw( +@@ -270,11 +274,15 @@ export interface vtkOpenGLTexture extends vtkViewNode { + depth: number, + numComps: number, + dataType: VtkDataTypes, +- data: any ++ data: any, ++ updatedExtents?: Extent[] + ): boolean; + + /** + * Creates a 3D filterable texture from raw data, with a preference for size over accuracy if necessary. ++ * ++ * updatedExtents is currently incompatible with webgl1, since there's no extent scaling. ++ * + * @param width The width of the texture. + * @param height The height of the texture. + * @param depth The depth of the texture. +@@ -282,6 +290,7 @@ export interface vtkOpenGLTexture extends vtkViewNode { + * @param dataType The data type of the texture. + * @param values The raw data for the texture. + * @param preferSizeOverAccuracy Whether to prefer texture size over accuracy. ++ * @param updatedExtents Only update the specified extents (default: []) + * @returns {boolean} True if the texture was successfully created, false otherwise. + */ + create3DFilterableFromRaw( +@@ -291,16 +300,21 @@ export interface vtkOpenGLTexture extends vtkViewNode { + numComps: number, + dataType: VtkDataTypes, + values: any, +- preferSizeOverAccuracy: boolean ++ preferSizeOverAccuracy: boolean, ++ updatedExtents?: Extent[] + ): boolean; + + /** + * Creates a 3D filterable texture from a data array, with a preference for size over accuracy if necessary. ++ * ++ * updatedExtents is currently incompatible with webgl1, since there's no extent scaling. ++ * + * @param width The width of the texture. + * @param height The height of the texture. + * @param depth The depth of the texture. + * @param dataArray The data array to use for the texture. + * @param preferSizeOverAccuracy Whether to prefer texture size over accuracy. ++ * @param updatedExtents Only update the specified extents (default: []) + * @returns {boolean} True if the texture was successfully created, false otherwise. + */ + create3DFilterableFromDataArray( +@@ -308,7 +322,8 @@ export interface vtkOpenGLTexture extends vtkViewNode { + height: number, + depth: number, + dataArray: any, +- preferSizeOverAccuracy: boolean ++ preferSizeOverAccuracy: boolean, ++ updatedExtents?: Extent[] + ): boolean; + + /** +diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.js b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.js +index 7d579af..68a34b0 100644 +--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.js ++++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/Texture.js +@@ -141,6 +141,7 @@ function vtkOpenGLTexture(publicAPI, model) { + if (model.context && model.handle) { + model.context.deleteTexture(model.handle); + } ++ model._paramCache = null; + model.handle = 0; + model.numberOfDimensions = 0; + model.target = 0; +@@ -202,6 +203,7 @@ function vtkOpenGLTexture(publicAPI, model) { + rwin.activateTexture(publicAPI); + rwin.deactivateTexture(publicAPI); + model.context.deleteTexture(model.handle); ++ model._paramCache = null; + model.handle = 0; + model.numberOfDimensions = 0; + model.target = 0; +@@ -345,6 +347,7 @@ function vtkOpenGLTexture(publicAPI, model) { + + //---------------------------------------------------------------------------- + publicAPI.resetFormatAndType = () => { ++ model._paramCache = null; + model.format = 0; + model.internalFormat = 0; + model._forceInternalFormat = false; +@@ -491,21 +494,117 @@ function vtkOpenGLTexture(publicAPI, model) { + }; + + //---------------------------------------------------------------------------- ++ ++ /** ++ * Gets the extent's size. ++ * @param {Extent} extent ++ */ ++ function getExtentSize(extent) { ++ const [xmin, xmax, ymin, ymax, zmin, zmax] = extent; ++ return [xmax - xmin + 1, ymax - ymin + 1, zmax - zmin + 1]; ++ } ++ ++ //---------------------------------------------------------------------------- ++ ++ /** ++ * Gets the number of pixels in the extent. ++ * @param {Extent} extent ++ */ ++ function getExtentPixelCount(extent) { ++ const [sx, sy, sz] = getExtentSize(extent); ++ return sx * sy * sz; ++ } ++ ++ //---------------------------------------------------------------------------- ++ ++ /** ++ * Reads a flattened extent from the image data and writes to the given output array. ++ * ++ * Assumes X varies the fastest and Z varies the slowest. ++ * ++ * @param {*} data ++ * @param {*} dataDims ++ * @param {Extent} extent ++ * @param {TypedArray} outArray ++ * @param {number} outOffset ++ * @returns ++ */ ++ function readExtentIntoArray(data, dataDims, extent, outArray, outOffset) { ++ const [xmin, xmax, ymin, ymax, zmin, zmax] = extent; ++ const [dx, dy] = dataDims; ++ const sxy = dx * dy; ++ let writeOffset = outOffset; ++ for (let zi = zmin; zi <= zmax; zi++) { ++ const zOffset = zi * sxy; ++ for (let yi = ymin; yi <= ymax; yi++) { ++ const zyOffset = zOffset + yi * dx; ++ // explicit alternative to data.subarray, ++ // due to potential perf issues on v8 ++ for (let readOffset = zyOffset + xmin, end = zyOffset + xmax; readOffset <= end; readOffset++, writeOffset++) { ++ outArray[writeOffset] = data[readOffset]; ++ } ++ } ++ } ++ } ++ ++ //---------------------------------------------------------------------------- ++ ++ /** ++ * Reads several image extents into a contiguous pixel array. ++ * ++ * @param {*} data ++ * @param {Extent[]} extent ++ * @param {TypedArrayConstructor} typedArrayConstructor optional typed array constructor ++ * @returns ++ */ ++ function readExtents(data, extents) { ++ let typedArrayConstructor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; ++ const constructor = typedArrayConstructor || data.constructor; ++ const numPixels = extents.reduce((count, extent) => count + getExtentPixelCount(extent), 0); ++ const extentPixels = new constructor(numPixels); ++ const dataDims = [model.width, model.height, model.depth]; ++ let writeOffset = 0; ++ extents.forEach(extent => { ++ readExtentIntoArray(data, dataDims, extent, extentPixels, writeOffset); ++ writeOffset += getExtentPixelCount(extent); ++ }); ++ return extentPixels; ++ } ++ ++ //---------------------------------------------------------------------------- ++ ++ /** ++ * Modifies the typed array types for webgl based on the given dataType. ++ * ++ * When sub-image extents are provided, the extents are all concatenated together into one typed array in the return output. ++ * ++ * @param {VtkDataTypes} dataType the VTK data type ++ * @param {TypedArray[]} data An array of typed arrays, one for each texture ++ * @param {boolean} depth should depth be used (default: false) ++ * @param {Array} imageExtents only consider these image extents (default: []) ++ * @returns an array of newly typed arrays, one for each texture ++ */ + function updateArrayDataType(dataType, data) { + let depth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; ++ let imageExtents = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; + const pixData = []; + let pixCount = model.width * model.height * model.components; + if (depth) { + pixCount *= model.depth; + } ++ const onlyUpdateExtents = !!imageExtents.length; + + // if the opengl data type is float + // then the data array must be float + if (dataType !== VtkDataTypes.FLOAT && model.openGLDataType === model.context.FLOAT) { + for (let idx = 0; idx < data.length; idx++) { + if (data[idx]) { +- const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx]; +- pixData.push(new Float32Array(dataArrayToCopy)); ++ if (onlyUpdateExtents) { ++ pixData.push(readExtents(data[idx], imageExtents, Float32Array)); ++ } else { ++ const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx]; ++ pixData.push(new Float32Array(dataArrayToCopy)); ++ } + } else { + pixData.push(null); + } +@@ -517,8 +616,12 @@ function vtkOpenGLTexture(publicAPI, model) { + if (dataType !== VtkDataTypes.UNSIGNED_CHAR && model.openGLDataType === model.context.UNSIGNED_BYTE) { + for (let idx = 0; idx < data.length; idx++) { + if (data[idx]) { +- const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx]; +- pixData.push(new Uint8Array(dataArrayToCopy)); ++ if (onlyUpdateExtents) { ++ pixData.push(readExtents(data[idx], imageExtents, Uint8Array)); ++ } else { ++ const dataArrayToCopy = data[idx].length > pixCount ? data[idx].subarray(0, pixCount) : data[idx]; ++ pixData.push(new Uint8Array(dataArrayToCopy)); ++ } + } else { + pixData.push(null); + } +@@ -537,9 +640,10 @@ function vtkOpenGLTexture(publicAPI, model) { + if (halfFloat) { + for (let idx = 0; idx < data.length; idx++) { + if (data[idx]) { +- const newArray = new Uint16Array(pixCount); +- const src = data[idx]; +- for (let i = 0; i < pixCount; i++) { ++ const src = onlyUpdateExtents ? readExtents(data[idx], imageExtents) : data[idx]; ++ const newArray = new Uint16Array(onlyUpdateExtents ? src.length : pixCount); ++ const newArrayLen = newArray.length; ++ for (let i = 0; i < newArrayLen; i++) { + newArray[i] = toHalf(src[i]); + } + pixData.push(newArray); +@@ -552,7 +656,7 @@ function vtkOpenGLTexture(publicAPI, model) { + // The output has to be filled + if (pixData.length === 0) { + for (let i = 0; i < data.length; i++) { +- pixData.push(data[i]); ++ pixData.push(onlyUpdateExtents && data[i] ? readExtents(data[i], imageExtents) : data[i]); + } + } + return pixData; +@@ -980,7 +1084,8 @@ function vtkOpenGLTexture(publicAPI, model) { + publicAPI.create2DFromRaw(width, height, numComps, dataType, data); + }; + //---------------------------------------------------------------------------- +- publicAPI.create3DFromRaw = (width, height, depth, numComps, dataType, data) => { ++ publicAPI.create3DFromRaw = function (width, height, depth, numComps, dataType, data) { ++ let updatedExtents = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : []; + // Permit OpenGLDataType to be half float, if applicable, for 3D + publicAPI.getOpenGLDataType(dataType); + +@@ -1000,25 +1105,52 @@ function vtkOpenGLTexture(publicAPI, model) { + model._openGLRenderWindow.activateTexture(publicAPI); + publicAPI.createTexture(); + publicAPI.bind(); ++ const hasUpdatedExtents = updatedExtents.length > 0; ++ const paramCache = model._paramCache; ++ ++ // It's possible for the texture parameters to change while ++ // streaming, so check for such a change. ++ const rebuildEntireTexture = !hasUpdatedExtents || !paramCache || !paramCache.tex3dAllocated || paramCache.prevInternalFormat !== model.internalFormat || paramCache.prevFormat !== model.format || paramCache.prevOpenGLDataType !== model.openGLDataType || paramCache.prevWidth !== model.width || paramCache.prevHeight !== model.height; ++ + // Create an array of texture with one texture + const dataArray = [data]; + const is3DArray = true; +- const pixData = updateArrayDataType(dataType, dataArray, is3DArray); ++ const pixData = updateArrayDataType(dataType, dataArray, is3DArray, ++ // if we need to reallocate the entire texture, then process the entire volume ++ rebuildEntireTexture ? [] : updatedExtents); + const scaledData = scaleTextureToHighestPowerOfTwo(pixData); + + // Source texture data from the PBO. + // model.context.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + model.context.pixelStorei(model.context.UNPACK_ALIGNMENT, 1); +- +- // openGLDataType +- +- if (useTexStorage(dataType)) { +- model.context.texStorage3D(model.target, 1, model.internalFormat, model.width, model.height, model.depth); +- if (scaledData[0] != null) { +- model.context.texSubImage3D(model.target, 0, 0, 0, 0, model.width, model.height, model.depth, model.format, model.openGLDataType, scaledData[0]); ++ if (rebuildEntireTexture) { ++ if (useTexStorage(dataType)) { ++ model.context.texStorage3D(model.target, 1, model.internalFormat, model.width, model.height, model.depth); ++ if (scaledData[0] != null) { ++ model.context.texSubImage3D(model.target, 0, 0, 0, 0, model.width, model.height, model.depth, model.format, model.openGLDataType, scaledData[0]); ++ } ++ } else { ++ model.context.texImage3D(model.target, 0, model.internalFormat, model.width, model.height, model.depth, 0, model.format, model.openGLDataType, scaledData[0]); ++ } ++ model._paramCache = { ++ tex3dAllocated: true, ++ prevInternalFormat: model.internalFormat, ++ prevFormat: model.format, ++ prevOpenGLDataType: model.openGLDataType, ++ prevWidth: model.width, ++ prevHeight: model.height ++ }; ++ } else if (hasUpdatedExtents) { ++ const extentPixels = scaledData[0]; ++ let readOffset = 0; ++ for (let i = 0; i < updatedExtents.length; i++) { ++ const extent = updatedExtents[i]; ++ const extentSize = getExtentSize(extent); ++ const extentPixelCount = getExtentPixelCount(extent); ++ const textureData = new extentPixels.constructor(extentPixels.buffer, readOffset, extentPixelCount); ++ readOffset += textureData.byteLength; ++ model.context.texSubImage3D(model.target, 0, extent[0], extent[2], extent[4], extentSize[0], extentSize[1], extentSize[2], model.format, model.openGLDataType, textureData); + } +- } else { +- model.context.texImage3D(model.target, 0, model.internalFormat, model.width, model.height, model.depth, 0, model.format, model.openGLDataType, scaledData[0]); + } + if (model.generateMipmap) { + model.context.generateMipmap(model.target); +@@ -1033,17 +1165,19 @@ function vtkOpenGLTexture(publicAPI, model) { + // Prefer create3DFilterableFromDataArray to enable caching of min and max values + publicAPI.create3DFilterableFromRaw = function (width, height, depth, numberOfComponents, dataType, values) { + let preferSizeOverAccuracy = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; ++ let updatedExtents = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : []; + return publicAPI.create3DFilterableFromDataArray(width, height, depth, vtkDataArray.newInstance({ + numberOfComponents, + dataType, + values +- }), preferSizeOverAccuracy); ++ }), preferSizeOverAccuracy, updatedExtents); + }; + + //---------------------------------------------------------------------------- + // This method create a 3D texture from dimensions and a DataArray + publicAPI.create3DFilterableFromDataArray = function (width, height, depth, dataArray) { + let preferSizeOverAccuracy = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; ++ let updatedExtents = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; + const { + numComps, + dataType, +@@ -1084,22 +1218,25 @@ function vtkOpenGLTexture(publicAPI, model) { + for (let c = 0; c < numComps; ++c) { + model.volumeInfo.scale[c] = 32767.0; + } +- return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data); ++ return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data, updatedExtents); + } + if (model.oglNorm16Ext && !model.useHalfFloat && dataType === VtkDataTypes.UNSIGNED_SHORT) { + for (let c = 0; c < numComps; ++c) { + model.volumeInfo.scale[c] = 65535.0; + } +- return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data); ++ return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data, updatedExtents); + } + if (dataType === VtkDataTypes.FLOAT || model.useHalfFloat && (dataType === VtkDataTypes.SHORT || dataType === VtkDataTypes.UNSIGNED_SHORT)) { +- return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data); ++ return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data, updatedExtents); + } + if (dataType === VtkDataTypes.UNSIGNED_CHAR) { + for (let c = 0; c < numComps; ++c) { + model.volumeInfo.scale[c] = 255.0; + } +- return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data); ++ return publicAPI.create3DFromRaw(width, height, depth, numComps, dataType, data, updatedExtents); ++ } ++ if (updatedExtents.length) { ++ vtkWarningMacro('Cannot perform extent updates with the given data type'); + } + // otherwise convert to float + const newArray = new Float32Array(numPixelsIn * numComps); +@@ -1276,6 +1413,7 @@ function vtkOpenGLTexture(publicAPI, model) { + const DEFAULT_VALUES = { + _openGLRenderWindow: null, + _forceInternalFormat: false, ++ _paramCache: null, + context: null, + handle: 0, + sendParametersTime: null, +diff --git a/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js b/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js +index 56956b4..6fb8618 100644 +--- a/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js ++++ b/node_modules/@kitware/vtk.js/Rendering/OpenGL/VolumeMapper.js +@@ -1120,14 +1120,23 @@ function vtkOpenGLVolumeMapper(publicAPI, model) { + // rebuild the scalarTexture if the data has changed + toString = `${image.getMTime()}A${scalars.getMTime()}`; + const reBuildTex = !tex?.vtkObj?.getHandle() || tex?.hash !== toString; +- if (reBuildTex) { +- // Build the textures +- const dims = image.getDimensions(); ++ const hasUpdatedExtents = !!model.renderable.getUpdatedExtents().length; ++ ++ // reset the scalars texture if there are no updated extents ++ if (reBuildTex && !hasUpdatedExtents) { + // Use norm16 for scalar texture if the extension is available + model.scalarTexture.setOglNorm16Ext(model.context.getExtension('EXT_texture_norm16')); + model.scalarTexture.releaseGraphicsResources(model._openGLRenderWindow); + model.scalarTexture.resetFormatAndType(); +- model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, model.renderable.getPreferSizeOverAccuracy()); ++ } ++ if (reBuildTex || hasUpdatedExtents) { ++ // Build the textures ++ // If hasUpdatedExtents, then the texture is partially updated ++ const updatedExtents = [...model.renderable.getUpdatedExtents()]; ++ // clear the array to acknowledge the update. ++ model.renderable.setUpdatedExtents([]); ++ const dims = image.getDimensions(); ++ model.scalarTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, model.renderable.getPreferSizeOverAccuracy(), updatedExtents); + if (scalars) { + model._openGLRenderWindow.setGraphicsResourceForObject(scalars, model.scalarTexture, toString); + } diff --git a/patches/@vueuse+core+10.7.2.patch b/patches/@vueuse+core+10.7.2.patch new file mode 100644 index 000000000..196550399 --- /dev/null +++ b/patches/@vueuse+core+10.7.2.patch @@ -0,0 +1,39 @@ +diff --git a/node_modules/@vueuse/core/index.d.cts b/node_modules/@vueuse/core/index.d.cts +index b8d2bbe..1bd10af 100644 +--- a/node_modules/@vueuse/core/index.d.cts ++++ b/node_modules/@vueuse/core/index.d.cts +@@ -1852,7 +1852,7 @@ declare function useEventListener(target: M + * @param listener + * @param options + */ +-declare function useEventListener(target: InferEventTarget, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; ++declare function useEventListener(target: MaybeRefOrGetter | null | undefined>, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; + /** + * Register using addEventListener on mounted, and removeEventListener automatically on unmounted. + * +diff --git a/node_modules/@vueuse/core/index.d.mts b/node_modules/@vueuse/core/index.d.mts +index b8d2bbe..5107394 100644 +--- a/node_modules/@vueuse/core/index.d.mts ++++ b/node_modules/@vueuse/core/index.d.mts +@@ -1852,7 +1852,7 @@ declare function useEventListener(target: M + * @param listener + * @param options + */ +-declare function useEventListener(target: InferEventTarget, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; ++declare function useEventListener(target: MaybeRefOrGetteR | null | undefined>, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; + /** + * Register using addEventListener on mounted, and removeEventListener automatically on unmounted. + * +diff --git a/node_modules/@vueuse/core/index.d.ts b/node_modules/@vueuse/core/index.d.ts +index b8d2bbe..1bd10af 100644 +--- a/node_modules/@vueuse/core/index.d.ts ++++ b/node_modules/@vueuse/core/index.d.ts +@@ -1852,7 +1852,7 @@ declare function useEventListener(target: M + * @param listener + * @param options + */ +-declare function useEventListener(target: InferEventTarget, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; ++declare function useEventListener(target: MaybeRefOrGetter | null | undefined>, event: Arrayable, listener: Arrayable>, options?: MaybeRefOrGetter): Fn; + /** + * Register using addEventListener on mounted, and removeEventListener automatically on unmounted. + * diff --git a/src/actions/importDicomChunks.ts b/src/actions/importDicomChunks.ts new file mode 100644 index 000000000..43d659f76 --- /dev/null +++ b/src/actions/importDicomChunks.ts @@ -0,0 +1,27 @@ +import { Chunk } from '@/src/core/streaming/chunk'; +import DicomChunkImage from '@/src/core/streaming/dicomChunkImage'; +import { splitAndSort } from '@/src/io/dicom'; +import useChunkStore from '@/src/store/chunks'; + +export async function importDicomChunks(chunks: Chunk[]) { + // split into groups + const chunksByVolume = await splitAndSort(chunks, (chunk) => chunk.metaBlob!); + + // add to matching DICOM images + const chunkStore = useChunkStore(); + await Promise.all( + Object.entries(chunksByVolume).map(async ([id, groupedChunks]) => { + const image = + (chunkStore.chunkImageById[id] as DicomChunkImage) ?? + new DicomChunkImage(); + chunkStore.chunkImageById[id] = image; + + await image.addChunks(groupedChunks); + + // TODO(fli) REMOVE to be on-demand when the dataset is being viewed + image.startLoad(); + }) + ); + + return Object.keys(chunksByVolume); +} diff --git a/src/components/DataBrowser.vue b/src/components/DataBrowser.vue index 954e84c47..5db78edce 100644 --- a/src/components/DataBrowser.vue +++ b/src/components/DataBrowser.vue @@ -46,7 +46,36 @@ export default defineComponent({ () => imageStore.idList.filter((id) => isRegularImage(id)).length > 0 ); - const panels = ref([SAMPLE_DATA_KEY, DICOM_WEB_KEY]); + const panels = ref([SAMPLE_DATA_KEY]); + + watch(patients, (newPatients, oldPatients) => { + // Remove from panels to avoid error in vuetify group.ts + oldPatients.forEach((oldPatient) => { + if (!newPatients.find((p) => p.key === oldPatient.key)) { + removeFromArray(panels.value, oldPatient.key); + } + }); + }); + + const hideSampleData = computed(() => dataBrowserStore.hideSampleData); + watch(hideSampleData, (hide) => { + if (hide) { + // Remove from panels to avoid error in vuetify group.ts + removeFromArray(panels.value, SAMPLE_DATA_KEY); + } + }); + + watch( + computed(() => dicomWeb.isConfigured), + (configured) => { + if (configured) { + panels.value.push(DICOM_WEB_KEY); + } else { + // Remove from panels to avoid error in vuetify group.ts + removeFromArray(panels.value, DICOM_WEB_KEY); + } + } + ); watch( [hasAnonymousImages, patients] as const, @@ -64,8 +93,6 @@ export default defineComponent({ } ); - const hideSampleData = computed(() => dataBrowserStore.hideSampleData); - const deletePatient = (key: string) => { dicomStore.patientStudies[key] .flatMap((study) => dicomStore.studyVolumes[study]) diff --git a/src/components/MeasurementsToolList.vue b/src/components/MeasurementsToolList.vue index 17986e8a1..c41ce056d 100644 --- a/src/components/MeasurementsToolList.vue +++ b/src/components/MeasurementsToolList.vue @@ -10,6 +10,7 @@ import { MultipleSelectionState, } from '@/src/composables/useMultipleToolSelection'; import { useToolSelectionStore } from '@/src/store/tools/toolSelection'; +import type { Maybe } from '@/src/types'; import MeasurementToolDetails from './MeasurementToolDetails.vue'; import { AnnotationTool } from '../types/annotation-tool'; @@ -80,7 +81,7 @@ const selectionStore = useToolSelectionStore(); const { selectAll, deselectAll, selected, selectionState } = useMultipleToolSelection(tools); -const toggleSelectAll = (shouldSelectAll: boolean) => { +const toggleSelectAll = (shouldSelectAll: Maybe) => { if (shouldSelectAll) { selectAll(); } else { diff --git a/src/components/PatientStudyVolumeBrowser.vue b/src/components/PatientStudyVolumeBrowser.vue index ffed6b9bc..027f25f38 100644 --- a/src/components/PatientStudyVolumeBrowser.vue +++ b/src/components/PatientStudyVolumeBrowser.vue @@ -1,9 +1,10 @@ @@ -74,7 +65,8 @@ import VtkOrientationMarker from '@/src/components/vtk/VtkOrientationMarker.vue' import ViewOverlayGrid from '@/src/components/ViewOverlayGrid.vue'; import useVolumeColoringStore from '@/src/store/view-configs/volume-coloring'; import { useResetViewsEvents } from '@/src/components/tools/ResetViews.vue'; -import { whenever } from '@vueuse/core'; +import { watchImmediate } from '@vueuse/core'; +import { onVTKEvent } from '@/src/composables/onVTKEvent'; interface Props extends LayoutViewProps { viewDirection: LPSAxisDir; @@ -99,14 +91,13 @@ useWebGLWatchdog(vtkView); useViewAnimationListener(vtkView, viewId, viewType); // base image -const { currentImageID, isImageLoading } = useCurrentImage(); +const { currentImageID, currentImageData } = useCurrentImage(); -whenever( - computed(() => !isImageLoading.value), - () => { - resetCamera(); - } -); +watchImmediate(currentImageID, () => resetCamera()); + +onVTKEvent(currentImageData, 'onModified', () => { + vtkView.value?.requestRender(); +}); // color preset const coloringStore = useVolumeColoringStore(); diff --git a/src/components/vtk/VtkBaseVolumeRepresentation.vue b/src/components/vtk/VtkBaseVolumeRepresentation.vue index 599156838..c4d466bc1 100644 --- a/src/components/vtk/VtkBaseVolumeRepresentation.vue +++ b/src/components/vtk/VtkBaseVolumeRepresentation.vue @@ -1,5 +1,5 @@