volview 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/.browserslistrc +4 -0
  2. package/.editorconfig +6 -0
  3. package/.env +6 -0
  4. package/.eslintrc.js +109 -0
  5. package/.gitattributes +1 -0
  6. package/.github/workflows/checks.yml +16 -0
  7. package/.github/workflows/publish.yml +52 -0
  8. package/.prettierignore +5 -0
  9. package/CONTRIBUTING.md +29 -0
  10. package/LICENSE +202 -0
  11. package/README.md +44 -0
  12. package/babel.config.js +3 -0
  13. package/documentation/config.js +15 -0
  14. package/documentation/content/KWVolViewLogo.png +0 -0
  15. package/documentation/content/KWVolViewLogo.svg +35 -0
  16. package/documentation/content/VolViewLogo.png +0 -0
  17. package/documentation/content/VolViewLogo.svg +22 -0
  18. package/documentation/content/doc/index.md +64 -0
  19. package/documentation/content/doc/loading_data.md +30 -0
  20. package/documentation/content/doc/mouse_controls.md +34 -0
  21. package/documentation/content/doc/quick_start_guide.md +75 -0
  22. package/documentation/content/doc/rendering.md +27 -0
  23. package/documentation/content/doc/state_files.md +18 -0
  24. package/documentation/content/doc/toolbar.md +20 -0
  25. package/documentation/content/doc/welcome_screen.md +23 -0
  26. package/documentation/content/gallery/01-volview-welcome-notes.jpg +0 -0
  27. package/documentation/content/gallery/01-volview-welcome.jpg +0 -0
  28. package/documentation/content/gallery/02-volview-about.jpg +0 -0
  29. package/documentation/content/gallery/02-volview-notifications.jpg +0 -0
  30. package/documentation/content/gallery/02-volview-settings.jpg +0 -0
  31. package/documentation/content/gallery/03-volview-sample-datasets.jpg +0 -0
  32. package/documentation/content/gallery/04-volview-active-data.jpg +0 -0
  33. package/documentation/content/gallery/05-volview-select-data-to-delete.jpg +0 -0
  34. package/documentation/content/gallery/06-volview-corner-annotation.jpg +0 -0
  35. package/documentation/content/gallery/07-volview-layout-notes.jpg +0 -0
  36. package/documentation/content/gallery/07-volview-layout.jpg +0 -0
  37. package/documentation/content/gallery/09-volview-layout-3DPrimary.jpg +0 -0
  38. package/documentation/content/gallery/10-volview-wl-pan-zoom-notes.jpg +0 -0
  39. package/documentation/content/gallery/10-volview-wl-pan-zoom.jpg +0 -0
  40. package/documentation/content/gallery/11-volview-paint-notes.jpg +0 -0
  41. package/documentation/content/gallery/11-volview-paint.jpg +0 -0
  42. package/documentation/content/gallery/12-volview-paint-options.jpg +0 -0
  43. package/documentation/content/gallery/13-volview-crop-notes.jpg +0 -0
  44. package/documentation/content/gallery/13-volview-crop.jpg +0 -0
  45. package/documentation/content/gallery/14-volview-ruler.jpg +0 -0
  46. package/documentation/content/gallery/15-volview-crosshairs.jpg +0 -0
  47. package/documentation/content/gallery/16-volview-rendering.jpg +0 -0
  48. package/documentation/content/gallery/17-volview-colormap-notes.jpg +0 -0
  49. package/documentation/content/gallery/17-volview-colormap.jpg +0 -0
  50. package/documentation/content/gallery/17-volview-opacity-notes.jpg +0 -0
  51. package/documentation/content/gallery/17-volview-opacity.jpg +0 -0
  52. package/documentation/content/gallery/17-volview-opacity2.jpg +0 -0
  53. package/documentation/content/gallery/18-volview-presets.jpg +0 -0
  54. package/documentation/content/gallery/19-volview-color.jpg +0 -0
  55. package/documentation/content/gallery/19-volview-color2.jpg +0 -0
  56. package/documentation/content/gallery/20-volview-lightfollowcamera0.jpg +0 -0
  57. package/documentation/content/gallery/20-volview-lightfollowcamera1.jpg +0 -0
  58. package/documentation/content/gallery/20-volview-lightfollowcamera2.jpg +0 -0
  59. package/documentation/content/gallery/20-volview-lightfollowcamera3.jpg +0 -0
  60. package/documentation/content/gallery/21-volview-hybrid-final.jpg +0 -0
  61. package/documentation/content/gallery/21-volview-hybrid0.0.jpg +0 -0
  62. package/documentation/content/gallery/21-volview-hybrid0.5.jpg +0 -0
  63. package/documentation/content/gallery/21-volview-hybrid1.0.jpg +0 -0
  64. package/documentation/content/gallery/LocalAmbientOcclusion.jpg +0 -0
  65. package/documentation/content/gallery/Notes.pptx +0 -0
  66. package/documentation/content/gallery/VolView-Overview.jpg +0 -0
  67. package/documentation/content/gallery/index.md +53 -0
  68. package/documentation/content/icon/favicon-16x16.png +0 -0
  69. package/documentation/content/icon/favicon-196x196.png +0 -0
  70. package/documentation/content/icon/favicon-32x32.png +0 -0
  71. package/documentation/content/icon/favicon-96x96.png +0 -0
  72. package/documentation/content/index.pug +43 -0
  73. package/documentation/content/logo.png +0 -0
  74. package/documentation/content/logo.svg +20 -0
  75. package/documentation/data/menu.yml +3 -0
  76. package/documentation/themes/navy/layout/partial/footer.swig +22 -0
  77. package/documentation/tpl/__en__ +31 -0
  78. package/documentation/tpl/__sidebar__ +11 -0
  79. package/package.json +96 -0
  80. package/patches/jsdom-global+3.0.2.patch +12 -0
  81. package/patches/vue-toastification+1.7.14.patch +41 -0
  82. package/patches/vuetify+2.6.10.patch +13 -0
  83. package/prettier.config.js +9 -0
  84. package/public/favicon.ico +0 -0
  85. package/public/global.css +20 -0
  86. package/public/index.html +28 -0
  87. package/public/pvm-sample.png +0 -0
  88. package/server/helper.py +147 -0
  89. package/server/host.js +134 -0
  90. package/server/serialize.js +111 -0
  91. package/server/serialize.py +203 -0
  92. package/server/server.py +84 -0
  93. package/server/test.py +17 -0
  94. package/src/assets/KitwareHeadAndNeck.jpg +0 -0
  95. package/src/assets/logo.png +0 -0
  96. package/src/assets/logo.svg +20 -0
  97. package/src/assets/samples/3DUS-Fetus.jpg +0 -0
  98. package/src/assets/samples/CTA-Head_and_Neck.jpg +0 -0
  99. package/src/assets/samples/MRA-Head_and_Neck.jpg +0 -0
  100. package/src/assets/samples/MRI-Cardiac.jpg +0 -0
  101. package/src/assets/samples/MRI-PROSTATEx.jpg +0 -0
  102. package/src/components/AboutBox.vue +157 -0
  103. package/src/components/AiModule.vue +60 -0
  104. package/src/components/AnnotationsModule.vue +37 -0
  105. package/src/components/App.vue +539 -0
  106. package/src/components/ColorFunctionSlider.vue +181 -0
  107. package/src/components/DataBrowser.vue +173 -0
  108. package/src/components/DragAndDrop.vue +107 -0
  109. package/src/components/GroupableItem.vue +30 -0
  110. package/src/components/ImageDataBrowser.vue +186 -0
  111. package/src/components/ImageListCard.vue +114 -0
  112. package/src/components/ItemGroup.vue +74 -0
  113. package/src/components/LabelmapList.vue +36 -0
  114. package/src/components/LayoutGrid.vue +96 -0
  115. package/src/components/MeasurementsList.vue +134 -0
  116. package/src/components/MessageCenter.vue +189 -0
  117. package/src/components/MessageItem.vue +83 -0
  118. package/src/components/MessageNotificationContent.vue +20 -0
  119. package/src/components/MessageNotifications.vue +84 -0
  120. package/src/components/ModelBrowser.vue +83 -0
  121. package/src/components/ModulePanel.vue +137 -0
  122. package/src/components/PaintControls.vue +174 -0
  123. package/src/components/PatientBrowser.vue +280 -0
  124. package/src/components/PatientStudyVolumeBrowser.vue +325 -0
  125. package/src/components/RenderingModule.vue +49 -0
  126. package/src/components/ResizableNavDrawer.vue +158 -0
  127. package/src/components/SampleDataBrowser.vue +198 -0
  128. package/src/components/SaveSession.vue +57 -0
  129. package/src/components/Settings.vue +35 -0
  130. package/src/components/SliceSlider.vue +182 -0
  131. package/src/components/SpleenModule.vue +101 -0
  132. package/src/components/ToolButton.vue +58 -0
  133. package/src/components/ToolStrip.vue +186 -0
  134. package/src/components/ViewOverlayGrid.vue +84 -0
  135. package/src/components/VolumePresets.vue +165 -0
  136. package/src/components/VolumeProperties.vue +171 -0
  137. package/src/components/VolumeRendering.vue +365 -0
  138. package/src/components/VtkThreeView.vue +512 -0
  139. package/src/components/VtkTwoView.vue +665 -0
  140. package/src/components/__tests__/ResizableNavDrawer.spec.js +32 -0
  141. package/src/components/__tests__/ToolButton.spec.js +46 -0
  142. package/src/components/icons/KitwareLogoIcon.vue +54 -0
  143. package/src/components/icons/VolViewFullLogo.vue +139 -0
  144. package/src/components/icons/VolViewLogo.vue +78 -0
  145. package/src/components/styles/utils.css +16 -0
  146. package/src/components/styles/vtk-view.css +116 -0
  147. package/src/components/tools/CropTool.vue +61 -0
  148. package/src/components/tools/CrosshairsTool.vue +93 -0
  149. package/src/components/tools/MouseManipulatorTool.vue +108 -0
  150. package/src/components/tools/PaintTool.vue +54 -0
  151. package/src/components/tools/PanTool.vue +35 -0
  152. package/src/components/tools/RulerTool.vue +202 -0
  153. package/src/components/tools/SliceScrollTool.vue +123 -0
  154. package/src/components/tools/WindowLevelTool.vue +191 -0
  155. package/src/components/tools/ZoomTool.vue +35 -0
  156. package/src/components/tools/crop/Crop2D.vue +367 -0
  157. package/src/components/tools/crop/Crop2DLineHandle.vue +103 -0
  158. package/src/components/tools/crop/Crop3D.vue +192 -0
  159. package/src/components/tools/crop/CropControls.vue +38 -0
  160. package/src/components/tools/crop/types.ts +12 -0
  161. package/src/components/tools/crosshairs/CrosshairSVG2D.vue +125 -0
  162. package/src/components/tools/crosshairs/CrosshairsWidget2D.vue +100 -0
  163. package/src/components/tools/paint/PaintWidget2D.vue +192 -0
  164. package/src/components/tools/ruler/RulerSVG2D.vue +181 -0
  165. package/src/components/tools/ruler/RulerWidget2D.vue +154 -0
  166. package/src/composables/__tests__/useDoubleRecord.spec.ts +57 -0
  167. package/src/composables/__tests__/useOrientationLabels.spec.ts +29 -0
  168. package/src/composables/isViewAnimating.ts +25 -0
  169. package/src/composables/manageVTKSubscription.ts +6 -0
  170. package/src/composables/onProxyManagerEvent.ts +50 -0
  171. package/src/composables/proxyManager.ts +13 -0
  172. package/src/composables/sync/__tests__/createPrimitiveSyncContext.spec.ts +41 -0
  173. package/src/composables/sync/__tests__/useWindowingSync.spec.ts +51 -0
  174. package/src/composables/sync/createPrimitiveSyncContext.ts +83 -0
  175. package/src/composables/sync/useWindowingSync.ts +48 -0
  176. package/src/composables/useAppLoadingNotifications.ts +73 -0
  177. package/src/composables/useCameraOrientation.ts +34 -0
  178. package/src/composables/useCurrentImage.ts +74 -0
  179. package/src/composables/useCurrentSlice.ts +43 -0
  180. package/src/composables/useDoubleRecord.ts +77 -0
  181. package/src/composables/useEventListener.ts +26 -0
  182. package/src/composables/useGlobalErrorHook.ts +18 -0
  183. package/src/composables/useMultiSelection.ts +29 -0
  184. package/src/composables/useOrientationLabels.ts +62 -0
  185. package/src/composables/usePanels.ts +42 -0
  186. package/src/composables/usePersistCameraConfig.ts +138 -0
  187. package/src/composables/useResizeObserver.ts +37 -0
  188. package/src/composables/useResizeToFit.ts +50 -0
  189. package/src/composables/useSceneBuilder.ts +96 -0
  190. package/src/composables/useToast.js +11 -0
  191. package/src/composables/useVTKCallback.ts +33 -0
  192. package/src/composables/useVTKWorldToDisplay.ts +141 -0
  193. package/src/composables/useViewProxy.ts +87 -0
  194. package/src/composables/useVolumeThumbnailing.ts +173 -0
  195. package/src/composables/useWebGLWatchdog.ts +32 -0
  196. package/src/composables/useWidgetManager.ts +41 -0
  197. package/src/config.ts +182 -0
  198. package/src/constants.ts +38 -0
  199. package/src/core/id.ts +18 -0
  200. package/src/core/provider.ts +31 -0
  201. package/src/core/proxies.ts +90 -0
  202. package/src/core/thumbnailers/__tests__/vtk-image.spec.ts +58 -0
  203. package/src/core/thumbnailers/index.ts +5 -0
  204. package/src/core/thumbnailers/volume-thumbnailer.ts +130 -0
  205. package/src/core/thumbnailers/vtk-image.ts +142 -0
  206. package/src/core/tools/__tests__/paint.spec.ts +101 -0
  207. package/src/core/tools/__tests__/ruler.spec.ts +73 -0
  208. package/src/core/tools/paint/brush.ts +20 -0
  209. package/src/core/tools/paint/ellipse-brush.ts +96 -0
  210. package/src/core/tools/paint/index.ts +141 -0
  211. package/src/core/tools/ruler.ts +149 -0
  212. package/src/global.d.ts +24 -0
  213. package/src/io/__tests__/io.spec.ts +26 -0
  214. package/src/io/dicom.ts +187 -0
  215. package/src/io/index.ts +11 -0
  216. package/src/io/io.ts +157 -0
  217. package/src/io/itk/itkConfig.js +8 -0
  218. package/src/io/itk-dicom/dicom.cpp +327 -0
  219. package/src/io/itk-dicom/web-build/dicom.js +16 -0
  220. package/src/io/itk-dicom/web-build/dicom.umd.js +21 -0
  221. package/src/io/itk-dicom/web-build/dicom.umd.wasm +0 -0
  222. package/src/io/itk-dicom/web-build/dicom.wasm +0 -0
  223. package/src/io/manifest.ts +30 -0
  224. package/src/io/readers.ts +36 -0
  225. package/src/io/state-file/index.ts +170 -0
  226. package/src/io/state-file/schema.ts +277 -0
  227. package/src/io/state-file/utils.ts +33 -0
  228. package/src/io/types.ts +4 -0
  229. package/src/io/vtk/async.reader.worker.ts +50 -0
  230. package/src/io/vtk/async.ts +58 -0
  231. package/src/io/vtk/async.writer.worker.ts +46 -0
  232. package/src/io/vtk/common.ts +30 -0
  233. package/src/io/zip.ts +31 -0
  234. package/src/main.js +66 -0
  235. package/src/main.ts +1 -0
  236. package/src/plugins/vuetify.js +25 -0
  237. package/src/shims-itk.d.ts +67 -0
  238. package/src/shims-misc.d.ts +4 -0
  239. package/src/shims-tsx.d.ts +13 -0
  240. package/src/shims-vtk.d.ts +1042 -0
  241. package/src/shims-vue.d.ts +5 -0
  242. package/src/store/__tests__/datasets.spec.ts +75 -0
  243. package/src/store/__tests__/messages.spec.ts +74 -0
  244. package/src/store/__tests__/rulers.spec.ts +77 -0
  245. package/src/store/__tests__/view-configs.spec.ts +37 -0
  246. package/src/store/datasets-dicom.ts +398 -0
  247. package/src/store/datasets-files.ts +35 -0
  248. package/src/store/datasets-images.ts +118 -0
  249. package/src/store/datasets-labelmaps.ts +136 -0
  250. package/src/store/datasets-models.ts +25 -0
  251. package/src/store/datasets.ts +341 -0
  252. package/src/store/messages.ts +144 -0
  253. package/src/store/tools/crop.ts +136 -0
  254. package/src/store/tools/crosshairs.ts +131 -0
  255. package/src/store/tools/index.ts +95 -0
  256. package/src/store/tools/paint.ts +191 -0
  257. package/src/store/tools/rulers.ts +305 -0
  258. package/src/store/tools/types.ts +54 -0
  259. package/src/store/view-configs/camera.ts +51 -0
  260. package/src/store/view-configs/common.ts +61 -0
  261. package/src/store/view-configs/slicing.ts +86 -0
  262. package/src/store/view-configs/types.ts +39 -0
  263. package/src/store/view-configs/volume-coloring.ts +193 -0
  264. package/src/store/view-configs/windowing.ts +69 -0
  265. package/src/store/view-configs.ts +78 -0
  266. package/src/store/views.ts +130 -0
  267. package/src/types/index.ts +16 -0
  268. package/src/types/layout.ts +10 -0
  269. package/src/types/lps.ts +40 -0
  270. package/src/types/views.ts +65 -0
  271. package/src/types/vtk-types.ts +29 -0
  272. package/src/utils/__tests__/lps.spec.ts +102 -0
  273. package/src/utils/color.ts +23 -0
  274. package/src/utils/errors.js +3 -0
  275. package/src/utils/fetch.ts +101 -0
  276. package/src/utils/hacks.ts +14 -0
  277. package/src/utils/index.ts +83 -0
  278. package/src/utils/lps.ts +129 -0
  279. package/src/utils/manipulators.ts +35 -0
  280. package/src/utils/promise-worker.ts +45 -0
  281. package/src/utils/vtk-helpers.ts +215 -0
  282. package/src/utils/workerHandler.js +42 -0
  283. package/src/vtk/ColorMaps.ts +72 -0
  284. package/src/vtk/CrosshairsWidget/behavior.ts +112 -0
  285. package/src/vtk/CrosshairsWidget/index.d.ts +22 -0
  286. package/src/vtk/CrosshairsWidget/index.js +73 -0
  287. package/src/vtk/CrosshairsWidget/state.ts +51 -0
  288. package/src/vtk/IJKSliceRepresentationProxy/index.d.ts +9 -0
  289. package/src/vtk/IJKSliceRepresentationProxy/index.js +59 -0
  290. package/src/vtk/LPSView2DProxy/index.d.ts +18 -0
  291. package/src/vtk/LPSView2DProxy/index.js +89 -0
  292. package/src/vtk/LPSView3DProxy/index.d.ts +16 -0
  293. package/src/vtk/LPSView3DProxy/index.js +78 -0
  294. package/src/vtk/LabelMap/index.d.ts +16 -0
  295. package/src/vtk/LabelMap/index.js +49 -0
  296. package/src/vtk/LabelMapSliceRepProxy/index.d.ts +6 -0
  297. package/src/vtk/LabelMapSliceRepProxy/index.js +104 -0
  298. package/src/vtk/MedicalColorPresets.json +1448 -0
  299. package/src/vtk/PaintBrushContextRepresentation/index.js +191 -0
  300. package/src/vtk/PaintWidget/behavior.ts +101 -0
  301. package/src/vtk/PaintWidget/index.d.ts +28 -0
  302. package/src/vtk/PaintWidget/index.js +56 -0
  303. package/src/vtk/PaintWidget/state.ts +41 -0
  304. package/src/vtk/PiecewiseWidget/index.js +203 -0
  305. package/src/vtk/RulerWidget/behavior.ts +185 -0
  306. package/src/vtk/RulerWidget/index.d.ts +25 -0
  307. package/src/vtk/RulerWidget/index.js +79 -0
  308. package/src/vtk/RulerWidget/state.ts +63 -0
  309. package/src/vtk/TreJsonConverter/index.js +150 -0
  310. package/src/vtk/proxy.js +66 -0
  311. package/src/vtk/proxyUtils.js +16 -0
  312. package/src/vtk/webvr-empty.js +1 -0
  313. package/tests/testUtils.js +12 -0
  314. package/tsconfig.json +46 -0
  315. package/vue.config.js +86 -0
@@ -0,0 +1,193 @@
1
+ import { MaybeRef } from '@vueuse/core';
2
+ import { useDoubleRecord } from '@/src/composables/useDoubleRecord';
3
+ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
4
+ import vtkPiecewiseFunctionProxy from '@kitware/vtk.js/Proxy/Core/PiecewiseFunctionProxy';
5
+ import {
6
+ getColorFunctionRangeFromPreset,
7
+ getOpacityFunctionFromPreset,
8
+ getOpacityRangeFromPreset,
9
+ } from '@/src/utils/vtk-helpers';
10
+ import { DEFAULT_PRESET_BY_MODALITY } from '@/src/config';
11
+ import { ColorTransferFunction } from '@/src/types/views';
12
+ import {
13
+ removeDataFromConfig,
14
+ removeViewFromConfig,
15
+ serializeViewConfig,
16
+ } from './common';
17
+ import { DEFAULT_PRESET } from '../../vtk/ColorMaps';
18
+ import { StateFile, ViewConfig } from '../../io/state-file/schema';
19
+ import { VolumeColorConfig } from './types';
20
+ import { useDICOMStore } from '../datasets-dicom';
21
+ import { useImageStore } from '../datasets-images';
22
+
23
+ export const DEFAULT_AMBIENT = 0.2;
24
+ export const DEFAULT_DIFFUSE = 0.7;
25
+ export const DEFAULT_SPECULAR = 0.3;
26
+
27
+ function getPresetFromImageModality(imageID: string) {
28
+ const dicomStore = useDICOMStore();
29
+ if (imageID in dicomStore.imageIDToVolumeKey) {
30
+ const volKey = dicomStore.imageIDToVolumeKey[imageID];
31
+ const { Modality } = dicomStore.volumeInfo[volKey];
32
+ if (Modality in DEFAULT_PRESET_BY_MODALITY) {
33
+ return DEFAULT_PRESET_BY_MODALITY[Modality];
34
+ }
35
+ }
36
+ return DEFAULT_PRESET;
37
+ }
38
+
39
+ export const defaultVolumeColorConfig = (): VolumeColorConfig => ({
40
+ colorBy: {
41
+ arrayName: '',
42
+ location: 'pointData',
43
+ },
44
+ transferFunction: {
45
+ preset: '',
46
+ mappingRange: [0, 1],
47
+ },
48
+ opacityFunction: {
49
+ mode: vtkPiecewiseFunctionProxy.Mode.Gaussians,
50
+ gaussians: [],
51
+ mappingRange: [0, 1],
52
+ },
53
+ cvr: {
54
+ enabled: true,
55
+ lightFollowsCamera: true,
56
+ useVolumetricScatteringBlending: false,
57
+ volumetricScatteringBlending: 0.5,
58
+ useLocalAmbientOcclusion: true,
59
+ laoKernelRadius: 6,
60
+ laoKernelSize: 3,
61
+ ambient: DEFAULT_AMBIENT,
62
+ diffuse: DEFAULT_DIFFUSE,
63
+ specular: DEFAULT_SPECULAR,
64
+ },
65
+ });
66
+
67
+ export const setupVolumeColorConfig = () => {
68
+ // (viewID, dataID) -> VolumeColorConfig
69
+ const volumeColorConfigs = useDoubleRecord<VolumeColorConfig>();
70
+
71
+ const getVolumeColorConfig = (viewID: string, dataID: string) =>
72
+ volumeColorConfigs.get(viewID, dataID);
73
+
74
+ const getComputedVolumeColorConfig = (
75
+ viewID: MaybeRef<string | null>,
76
+ dataID: MaybeRef<string | null>
77
+ ) => volumeColorConfigs.getComputed(viewID, dataID);
78
+
79
+ const updateVolumeColorConfig = (
80
+ viewID: string,
81
+ dataID: string,
82
+ update: Partial<VolumeColorConfig>
83
+ ) => {
84
+ const config = {
85
+ ...defaultVolumeColorConfig(),
86
+ ...volumeColorConfigs.get(viewID, dataID),
87
+ ...update,
88
+ };
89
+ volumeColorConfigs.set(viewID, dataID, config);
90
+ };
91
+
92
+ const createUpdateFunc = <K extends keyof VolumeColorConfig>(
93
+ key: K,
94
+ validator: (config: VolumeColorConfig[K]) => VolumeColorConfig[K] = (i) => i
95
+ ) => {
96
+ return (
97
+ viewID: string,
98
+ dataID: string,
99
+ update: Partial<VolumeColorConfig[K]>
100
+ ) => {
101
+ const config =
102
+ volumeColorConfigs.get(viewID, dataID) ?? defaultVolumeColorConfig();
103
+ const updatedConfig = validator({
104
+ ...config[key],
105
+ ...update,
106
+ });
107
+ updateVolumeColorConfig(viewID, dataID, { [key]: updatedConfig });
108
+ };
109
+ };
110
+
111
+ const updateVolumeColorBy = createUpdateFunc('colorBy');
112
+ const updateVolumeColorTransferFunction =
113
+ createUpdateFunc('transferFunction');
114
+ const updateVolumeOpacityFunction = createUpdateFunc('opacityFunction');
115
+ const updateVolumeCVRParameters = createUpdateFunc('cvr', (cvrConfig) => {
116
+ return {
117
+ ...cvrConfig,
118
+ // 2X kernel size minimizes flickering lighting
119
+ // Limit kernel radius to [2*size, 2*size+10]
120
+ laoKernelRadius: Math.max(
121
+ 2 * cvrConfig.laoKernelSize,
122
+ Math.min(2 * cvrConfig.laoKernelSize + 10, cvrConfig.laoKernelRadius)
123
+ ),
124
+ };
125
+ });
126
+
127
+ const setVolumeColorPreset = (
128
+ viewID: string,
129
+ imageID: string,
130
+ preset: string
131
+ ) => {
132
+ const imageStore = useImageStore();
133
+ const image = imageStore.dataIndex[imageID];
134
+ if (!image) return;
135
+ const imageDataRange = image.getPointData().getScalars().getRange();
136
+
137
+ const ctRange = getColorFunctionRangeFromPreset(preset);
138
+ const ctFunc: Partial<ColorTransferFunction> = {
139
+ preset,
140
+ mappingRange: ctRange || imageDataRange,
141
+ };
142
+ updateVolumeColorTransferFunction(viewID, imageID, ctFunc);
143
+
144
+ const opFunc = getOpacityFunctionFromPreset(preset);
145
+ const opRange = getOpacityRangeFromPreset(preset);
146
+ opFunc.mappingRange = opRange || imageDataRange;
147
+ updateVolumeOpacityFunction(viewID, imageID, opFunc);
148
+ };
149
+
150
+ const resetToDefaultColoring = (
151
+ viewID: string,
152
+ dataID: string,
153
+ image: vtkImageData
154
+ ) => {
155
+ const scalars = image.getPointData().getScalars();
156
+
157
+ updateVolumeColorBy(viewID, dataID, {
158
+ arrayName: scalars.getName(),
159
+ location: 'pointData',
160
+ });
161
+ setVolumeColorPreset(viewID, dataID, getPresetFromImageModality(dataID));
162
+ };
163
+
164
+ const serialize = (stateFile: StateFile) => {
165
+ serializeViewConfig(stateFile, getVolumeColorConfig, 'volumeColorConfig');
166
+ };
167
+
168
+ const deserialize = (viewID: string, config: Record<string, ViewConfig>) => {
169
+ Object.entries(config).forEach(([dataID, viewConfig]) => {
170
+ if (viewConfig.volumeColorConfig) {
171
+ volumeColorConfigs.set(viewID, dataID, viewConfig.volumeColorConfig);
172
+ }
173
+ });
174
+ };
175
+
176
+ return {
177
+ removeView: removeViewFromConfig(volumeColorConfigs),
178
+ removeData: removeDataFromConfig(volumeColorConfigs),
179
+ serialize,
180
+ deserialize,
181
+ actions: {
182
+ getVolumeColorConfig,
183
+ getComputedVolumeColorConfig,
184
+ updateVolumeColorConfig,
185
+ updateVolumeColorBy,
186
+ updateVolumeColorTransferFunction,
187
+ updateVolumeOpacityFunction,
188
+ updateVolumeCVRParameters,
189
+ resetToDefaultColoring,
190
+ setVolumeColorPreset,
191
+ },
192
+ };
193
+ };
@@ -0,0 +1,69 @@
1
+ import { useDoubleRecord } from '@/src/composables/useDoubleRecord';
2
+ import {
3
+ removeDataFromConfig,
4
+ removeViewFromConfig,
5
+ serializeViewConfig,
6
+ } from './common';
7
+ import { StateFile, ViewConfig } from '../../io/state-file/schema';
8
+ import { WindowLevelConfig } from './types';
9
+
10
+ export const defaultWindowLevelConfig = (): WindowLevelConfig => ({
11
+ width: 1,
12
+ level: 0.5,
13
+ min: 0,
14
+ max: 1,
15
+ });
16
+
17
+ export const setupWindowingConfig = () => {
18
+ // (viewID, dataID) -> WindowLevelConfig
19
+ const windowLevelConfigs = useDoubleRecord<WindowLevelConfig>();
20
+
21
+ const getWindowingConfig = (viewID: string, dataID: string) =>
22
+ windowLevelConfigs.get(viewID, dataID);
23
+
24
+ const updateWindowingConfig = (
25
+ viewID: string,
26
+ dataID: string,
27
+ update: Partial<WindowLevelConfig>
28
+ ) => {
29
+ const config = {
30
+ ...defaultWindowLevelConfig(),
31
+ ...windowLevelConfigs.get(viewID, dataID),
32
+ ...update,
33
+ };
34
+ windowLevelConfigs.set(viewID, dataID, config);
35
+ };
36
+
37
+ const resetWindowLevel = (viewID: string, dataID: string) => {
38
+ if (windowLevelConfigs.has(viewID, dataID)) {
39
+ const config = windowLevelConfigs.get(viewID, dataID)!;
40
+ const width = config.max - config.min;
41
+ const level = (config.max + config.min) / 2;
42
+ updateWindowingConfig(viewID, dataID, { width, level });
43
+ }
44
+ };
45
+
46
+ const serialize = (stateFile: StateFile) => {
47
+ serializeViewConfig(stateFile, getWindowingConfig, 'window');
48
+ };
49
+
50
+ const deserialize = (viewID: string, config: Record<string, ViewConfig>) => {
51
+ Object.entries(config).forEach(([dataID, viewConfig]) => {
52
+ if (viewConfig.window) {
53
+ windowLevelConfigs.set(viewID, dataID, viewConfig.window);
54
+ }
55
+ });
56
+ };
57
+
58
+ return {
59
+ removeView: removeViewFromConfig(windowLevelConfigs),
60
+ removeData: removeDataFromConfig(windowLevelConfigs),
61
+ serialize,
62
+ deserialize,
63
+ actions: {
64
+ getWindowingConfig,
65
+ updateWindowingConfig,
66
+ resetWindowLevel,
67
+ },
68
+ };
69
+ };
@@ -0,0 +1,78 @@
1
+ import { defineStore } from 'pinia';
2
+
3
+ import { setupSlicingConfig } from './view-configs/slicing';
4
+ import { setupWindowingConfig } from './view-configs/windowing';
5
+ import { setupCameraConfig } from './view-configs/camera';
6
+ import { setupVolumeColorConfig } from './view-configs/volume-coloring';
7
+ import { StateFile, ViewConfig } from '../io/state-file/schema';
8
+ import { useImageStore } from './datasets-images';
9
+
10
+ /**
11
+ * This store saves view configuration that is associated with a specific
12
+ * view. The key is a synthetic id generated from the view ID and data ID.
13
+ */
14
+ export const useViewConfigStore = defineStore('viewConfig', () => {
15
+ const sliceConfig = setupSlicingConfig();
16
+ const windowingConfig = setupWindowingConfig();
17
+ const cameraConfig = setupCameraConfig();
18
+ const volumeColorConfig = setupVolumeColorConfig();
19
+
20
+ const removeView = (viewID: string) => {
21
+ sliceConfig.removeView(viewID);
22
+ windowingConfig.removeView(viewID);
23
+ cameraConfig.removeView(viewID);
24
+ volumeColorConfig.removeView(viewID);
25
+ };
26
+
27
+ const removeData = (dataID: string, viewID?: string) => {
28
+ sliceConfig.removeData(dataID, viewID);
29
+ windowingConfig.removeData(dataID, viewID);
30
+ cameraConfig.removeData(dataID, viewID);
31
+ volumeColorConfig.removeData(dataID, viewID);
32
+ };
33
+
34
+ const serialize = (stateFile: StateFile) => {
35
+ sliceConfig.serialize(stateFile);
36
+ windowingConfig.serialize(stateFile);
37
+ cameraConfig.serialize(stateFile);
38
+ volumeColorConfig.serialize(stateFile);
39
+ };
40
+
41
+ const deserialize = (
42
+ viewID: string,
43
+ config: Record<string, ViewConfig>,
44
+ dataIDMap: Record<string, string>
45
+ ) => {
46
+ // First update the view config map to use the new dataIDs
47
+ const updatedConfig: Record<string, ViewConfig> = {};
48
+ Object.entries(config).forEach(([dataID, viewConfig]) => {
49
+ const newDataID = dataIDMap[dataID];
50
+ updatedConfig[newDataID] = viewConfig;
51
+ });
52
+
53
+ sliceConfig.deserialize(viewID, updatedConfig);
54
+ windowingConfig.deserialize(viewID, updatedConfig);
55
+ cameraConfig.deserialize(viewID, updatedConfig);
56
+ volumeColorConfig.deserialize(viewID, updatedConfig);
57
+ };
58
+
59
+ // delete hook
60
+ const imageStore = useImageStore();
61
+ imageStore.$onAction(({ name, args }) => {
62
+ if (name === 'deleteData') {
63
+ const [id] = args;
64
+ removeData(id);
65
+ }
66
+ });
67
+
68
+ return {
69
+ removeView,
70
+ removeData,
71
+ serialize,
72
+ deserialize,
73
+ ...sliceConfig.actions,
74
+ ...windowingConfig.actions,
75
+ ...cameraConfig.actions,
76
+ ...volumeColorConfig.actions,
77
+ };
78
+ });
@@ -0,0 +1,130 @@
1
+ import { set, del } from '@vue/composition-api';
2
+ import vtkAbstractRepresentationProxy from '@kitware/vtk.js/Proxy/Core/AbstractRepresentationProxy';
3
+ import vtkViewProxy from '@kitware/vtk.js/Proxy/Core/ViewProxy';
4
+ import { defineStore } from 'pinia';
5
+ import { DefaultViewSpec, InitViewSpecs } from '../config';
6
+ import { ViewProxyType } from '../core/proxies';
7
+ import { Layout, LayoutDirection } from '../types/layout';
8
+ import { useViewConfigStore } from './view-configs';
9
+ import { ViewSpec } from '../types/views';
10
+ import {
11
+ StateFile,
12
+ Layout as StateFileLayout,
13
+ View,
14
+ } from '../io/state-file/schema';
15
+
16
+ interface State {
17
+ layout: Layout;
18
+ viewSpecs: Record<string, ViewSpec>;
19
+ }
20
+
21
+ export const useViewStore = defineStore('view', {
22
+ state: (): State => ({
23
+ layout: {
24
+ direction: LayoutDirection.V,
25
+ items: [],
26
+ },
27
+ viewSpecs: structuredClone(InitViewSpecs),
28
+ }),
29
+ getters: {
30
+ viewIDs(state) {
31
+ return Object.keys(state.viewSpecs);
32
+ },
33
+ getViewProxy() {
34
+ return <T extends vtkViewProxy>(id: string) => {
35
+ return this.$proxies.getView<T>(id);
36
+ };
37
+ },
38
+ getDataRepresentationForView() {
39
+ return <T extends vtkAbstractRepresentationProxy>(
40
+ dataID: string,
41
+ viewID: string
42
+ ) => {
43
+ return <T | null>(
44
+ this.$proxies.getDataRepresentationForView(dataID, viewID)
45
+ );
46
+ };
47
+ },
48
+ },
49
+ actions: {
50
+ createOrGetViewProxy<T extends vtkViewProxy = vtkViewProxy>(
51
+ id: string,
52
+ type: ViewProxyType
53
+ ) {
54
+ return (
55
+ this.$proxies.getView<T>(id) ?? <T>this.$proxies.createView(id, type)
56
+ );
57
+ },
58
+ addView(id: string) {
59
+ if (!(id in this.viewSpecs)) {
60
+ set(this.viewSpecs, id, structuredClone(DefaultViewSpec));
61
+ }
62
+ },
63
+ removeView(id: string) {
64
+ if (id in this.viewSpecs) {
65
+ del(this.viewSpecs, id);
66
+ this.$proxies.removeView(id);
67
+ }
68
+ },
69
+ setLayout(layout: Layout) {
70
+ this.layout = layout;
71
+
72
+ const layoutsToProcess = [layout];
73
+ while (layoutsToProcess.length) {
74
+ const ly = layoutsToProcess.shift()!;
75
+ ly.items.forEach((item) => {
76
+ if (typeof item === 'string') {
77
+ // item is a view ID
78
+ this.addView(item);
79
+ } else {
80
+ layoutsToProcess.push(item);
81
+ }
82
+ });
83
+ }
84
+ },
85
+ serialize(stateFile: StateFile) {
86
+ const viewConfigStore = useViewConfigStore();
87
+ const { manifest } = stateFile;
88
+ const { views } = manifest;
89
+
90
+ manifest.layout = this.layout as StateFileLayout;
91
+
92
+ // Serialize the view specs
93
+ Object.entries(this.viewSpecs).forEach(([id, spec]) => {
94
+ const type = spec.viewType;
95
+ const { props } = spec;
96
+ const config = {};
97
+
98
+ const view = {
99
+ id,
100
+ type,
101
+ props,
102
+ config,
103
+ };
104
+
105
+ views.push(view);
106
+ });
107
+
108
+ // Serialize the view config
109
+ viewConfigStore.serialize(stateFile);
110
+ },
111
+ deserialize(views: View[], dataIDMap: Record<string, string>) {
112
+ const viewConfigStore = useViewConfigStore();
113
+
114
+ views.forEach((view) => {
115
+ const viewID = view.id;
116
+
117
+ const viewSpec = {
118
+ viewType: view.type,
119
+ props: view.props,
120
+ };
121
+
122
+ this.viewSpecs[viewID] = viewSpec;
123
+
124
+ // Now delegate the deserialization of the view config
125
+ const { config } = view;
126
+ viewConfigStore.deserialize(viewID, config, dataIDMap);
127
+ });
128
+ },
129
+ },
130
+ });
@@ -0,0 +1,16 @@
1
+ import { StoreDefinition } from 'pinia';
2
+
3
+ export type NullableValues<T> = {
4
+ [K in keyof T]: T[K] | null;
5
+ };
6
+
7
+ export type PiniaStoreState<S extends StoreDefinition> =
8
+ ReturnType<S>['$state'];
9
+
10
+ export type SampleDataset = {
11
+ name: string;
12
+ filename: string;
13
+ description: string;
14
+ url: string;
15
+ image: string;
16
+ };
@@ -0,0 +1,10 @@
1
+ export enum LayoutDirection {
2
+ V = 'V',
3
+ H = 'H',
4
+ }
5
+
6
+ export type Layout = {
7
+ direction: LayoutDirection;
8
+ items: Array<Layout | string>;
9
+ name?: string;
10
+ };
@@ -0,0 +1,40 @@
1
+ import { Vector2 } from '@kitware/vtk.js/types';
2
+ import { vec3 } from 'gl-matrix';
3
+
4
+ export type LPSAxis = 'Axial' | 'Sagittal' | 'Coronal';
5
+
6
+ export type LPSAxisDir =
7
+ | 'Left'
8
+ | 'Right'
9
+ | 'Posterior'
10
+ | 'Anterior'
11
+ | 'Superior'
12
+ | 'Inferior';
13
+
14
+ export interface LPSDirections {
15
+ // Maps LPS direction to world-space direction (not index-space direction)
16
+ // These should match columns of the current image orientation matrix.
17
+ Left: vec3;
18
+ Right: vec3;
19
+ Posterior: vec3;
20
+ Anterior: vec3;
21
+ Superior: vec3;
22
+ Inferior: vec3;
23
+
24
+ // maps LPS axis to column in direction matrix
25
+ Coronal: 0 | 1 | 2;
26
+ Sagittal: 0 | 1 | 2;
27
+ Axial: 0 | 1 | 2;
28
+ }
29
+
30
+ export interface LPSPoint {
31
+ Sagittal: number;
32
+ Coronal: number;
33
+ Axial: number;
34
+ }
35
+
36
+ export interface LPSBounds {
37
+ Sagittal: Vector2;
38
+ Coronal: Vector2;
39
+ Axial: Vector2;
40
+ }
@@ -0,0 +1,65 @@
1
+ import {
2
+ IPiecewiseFunctionProxyMode,
3
+ PiecewiseGaussian,
4
+ PiecewiseNode,
5
+ } from '@kitware/vtk.js/Proxy/Core/PiecewiseFunctionProxy';
6
+
7
+ export type ViewType = '2D' | '3D';
8
+
9
+ export interface ViewSpec {
10
+ viewType: ViewType;
11
+ props: Record<string, any>;
12
+ }
13
+
14
+ export interface ColorBy {
15
+ arrayName: string;
16
+ location: string;
17
+ }
18
+
19
+ export interface OpacityGaussians {
20
+ mode: IPiecewiseFunctionProxyMode.Gaussians;
21
+ gaussians: PiecewiseGaussian[];
22
+ mappingRange: [number, number];
23
+ }
24
+
25
+ export interface OpacityPoints {
26
+ mode: IPiecewiseFunctionProxyMode.Points;
27
+ // base preset that has the opacity points
28
+ preset: string;
29
+ shift: number;
30
+ mappingRange: [number, number];
31
+ }
32
+
33
+ export interface OpacityNodes {
34
+ mode: IPiecewiseFunctionProxyMode.Nodes;
35
+ nodes: PiecewiseNode[];
36
+ mappingRange: [number, number];
37
+ }
38
+
39
+ export type OpacityFunction = OpacityGaussians | OpacityPoints | OpacityNodes;
40
+
41
+ export interface ColorTransferFunction {
42
+ preset: string;
43
+ mappingRange: [number, number];
44
+ }
45
+ export interface ColoringConfig {
46
+ colorBy: ColorBy;
47
+ transferFunction: ColorTransferFunction;
48
+ opacityFunction: OpacityFunction;
49
+ }
50
+
51
+ export interface CVRConfig {
52
+ enabled: boolean;
53
+
54
+ lightFollowsCamera: boolean;
55
+
56
+ useVolumetricScatteringBlending: boolean;
57
+ volumetricScatteringBlending: number;
58
+
59
+ useLocalAmbientOcclusion: boolean;
60
+ laoKernelSize: number;
61
+ laoKernelRadius: number;
62
+ ambient: number;
63
+ diffuse: number;
64
+ specular: number;
65
+ }
@@ -0,0 +1,29 @@
1
+ import { vtkAlgorithm, vtkObject } from '@kitware/vtk.js/interfaces';
2
+ import vtkProxyManager from '@kitware/vtk.js/Proxy/Core/ProxyManager';
3
+ import vtkDataSet from '@kitware/vtk.js/Common/DataModel/DataSet';
4
+ import vtkLPSView2DProxy from '../vtk/LPSView2DProxy';
5
+ import vtkLPSView3DProxy from '../vtk/LPSView3DProxy';
6
+
7
+ export interface vtkClass {
8
+ newInstance: () => vtkObject;
9
+ extend: (publiAPI: any, model: any) => void;
10
+ }
11
+
12
+ export interface vtkReader extends vtkObject, vtkAlgorithm {
13
+ parseAsArrayBuffer: (ab: ArrayBufferLike) => void;
14
+ parseAsText: (text: string) => void;
15
+ }
16
+
17
+ export interface vtkWriter extends vtkObject {
18
+ write: (data: vtkDataSet) => any;
19
+ }
20
+
21
+ export default interface vtkProxyObject extends vtkObject {
22
+ getProxyId(): string;
23
+ getProxyGroup(): string;
24
+ getProxyName(): string;
25
+ getProxyManager(): vtkProxyManager;
26
+ setProxyManager(manager: vtkProxyManager): void;
27
+ }
28
+
29
+ export type vtkLPSViewProxy = vtkLPSView2DProxy | vtkLPSView3DProxy;