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,277 @@
1
+ import JSZip from 'jszip';
2
+ import { z } from 'zod';
3
+ import { Vector3 } from '@kitware/vtk.js/types';
4
+ import vtkPiecewiseFunctionProxy, {
5
+ PiecewiseGaussian,
6
+ PiecewiseNode,
7
+ } from '@kitware/vtk.js/Proxy/Core/PiecewiseFunctionProxy';
8
+
9
+ import {
10
+ Ruler,
11
+ Tools as ToolsEnum,
12
+ LPSCroppingPlanes,
13
+ } from '@/src/store/tools/types';
14
+ import { InteractionState } from '@/src/vtk/RulerWidget/state';
15
+
16
+ import {
17
+ CameraConfig,
18
+ SliceConfig,
19
+ WindowLevelConfig,
20
+ VolumeColorConfig,
21
+ } from '../../store/view-configs/types';
22
+ import { LPSAxisDir, LPSAxis } from '../../types/lps';
23
+ import { LayoutDirection } from '../../types/layout';
24
+ import {
25
+ ViewType,
26
+ ColorBy,
27
+ ColorTransferFunction,
28
+ OpacityFunction,
29
+ OpacityGaussians,
30
+ OpacityPoints,
31
+ OpacityNodes,
32
+ ColoringConfig,
33
+ CVRConfig,
34
+ } from '../../types/views';
35
+
36
+ export enum DataSetType {
37
+ DICOM = 'dicom',
38
+ IMAGE = 'image',
39
+ }
40
+
41
+ const DataSetTypeNative = z.nativeEnum(DataSetType);
42
+
43
+ const LPSAxisDir: z.ZodType<LPSAxisDir> = z.union([
44
+ z.literal('Left'),
45
+ z.literal('Right'),
46
+ z.literal('Posterior'),
47
+ z.literal('Anterior'),
48
+ z.literal('Superior'),
49
+ z.literal('Inferior'),
50
+ ]);
51
+
52
+ const DataSet = z.object({
53
+ id: z.string(),
54
+ path: z.string(),
55
+ type: DataSetTypeNative,
56
+ });
57
+ export type DataSet = z.infer<typeof DataSet>;
58
+
59
+ const LayoutDirectionNative = z.nativeEnum(LayoutDirection);
60
+
61
+ export interface Layout {
62
+ name?: string;
63
+ direction: LayoutDirection;
64
+ items: Array<Layout | string>;
65
+ }
66
+
67
+ const Layout: z.ZodType<Layout> = z.lazy(() =>
68
+ z.object({
69
+ name: z.string().optional(),
70
+ direction: LayoutDirectionNative,
71
+ items: z.array(z.union([Layout, z.string()])),
72
+ })
73
+ );
74
+
75
+ const Vector3: z.ZodType<Vector3> = z.tuple([
76
+ z.number(),
77
+ z.number(),
78
+ z.number(),
79
+ ]);
80
+
81
+ const WindowLevelConfig: z.ZodType<WindowLevelConfig> = z.object({
82
+ width: z.number(),
83
+ level: z.number(),
84
+ min: z.number(),
85
+ max: z.number(),
86
+ });
87
+
88
+ const SliceConfig: z.ZodType<SliceConfig> = z.object({
89
+ slice: z.number(),
90
+ min: z.number(),
91
+ max: z.number(),
92
+ axisDirection: LPSAxisDir,
93
+ });
94
+
95
+ const CameraConfig: z.ZodType<CameraConfig> = z.object({
96
+ parallelScale: z.number().optional(),
97
+ position: Vector3.optional(),
98
+ focalPoint: Vector3.optional(),
99
+ directionOfProjection: Vector3.optional(),
100
+ viewUp: Vector3.optional(),
101
+ });
102
+
103
+ const ColorBy: z.ZodType<ColorBy> = z.object({
104
+ arrayName: z.string(),
105
+ location: z.string(),
106
+ });
107
+
108
+ const PiecewiseGaussian: z.ZodType<PiecewiseGaussian> = z.object({
109
+ position: z.number(),
110
+ height: z.number(),
111
+ width: z.number(),
112
+ xBias: z.number(),
113
+ yBias: z.number(),
114
+ });
115
+
116
+ const PiecewiseNode: z.ZodType<PiecewiseNode> = z.object({
117
+ x: z.number(),
118
+ y: z.number(),
119
+ midpoint: z.number(),
120
+ sharpness: z.number(),
121
+ });
122
+
123
+ const OpacityGaussians: z.ZodType<OpacityGaussians> = z.object({
124
+ mode: z.literal(vtkPiecewiseFunctionProxy.Mode.Gaussians),
125
+ gaussians: PiecewiseGaussian.array(),
126
+ mappingRange: z.tuple([z.number(), z.number()]),
127
+ });
128
+
129
+ const OpacityPoints: z.ZodType<OpacityPoints> = z.object({
130
+ mode: z.literal(vtkPiecewiseFunctionProxy.Mode.Points),
131
+ preset: z.string(),
132
+ shift: z.number(),
133
+ mappingRange: z.tuple([z.number(), z.number()]),
134
+ });
135
+
136
+ const OpacityNodes: z.ZodType<OpacityNodes> = z.object({
137
+ mode: z.literal(vtkPiecewiseFunctionProxy.Mode.Nodes),
138
+ nodes: PiecewiseNode.array(),
139
+ mappingRange: z.tuple([z.number(), z.number()]),
140
+ });
141
+
142
+ const OpacityFunction = z.union([
143
+ OpacityGaussians,
144
+ OpacityPoints,
145
+ OpacityNodes,
146
+ ]);
147
+
148
+ const ColorTransferFunction: z.ZodType<ColorTransferFunction> = z.object({
149
+ preset: z.string(),
150
+ mappingRange: z.tuple([z.number(), z.number()]),
151
+ });
152
+
153
+ const ColoringConfig: z.ZodType<ColoringConfig> = z.object({
154
+ colorBy: ColorBy,
155
+ transferFunction: ColorTransferFunction,
156
+ opacityFunction: OpacityFunction,
157
+ });
158
+
159
+ const CVRConfig: z.ZodType<CVRConfig> = z.object({
160
+ enabled: z.boolean(),
161
+ lightFollowsCamera: z.boolean(),
162
+ useVolumetricScatteringBlending: z.boolean(),
163
+ volumetricScatteringBlending: z.number(),
164
+ useLocalAmbientOcclusion: z.boolean(),
165
+ laoKernelSize: z.number(),
166
+ laoKernelRadius: z.number(),
167
+ ambient: z.number(),
168
+ diffuse: z.number(),
169
+ specular: z.number(),
170
+ });
171
+
172
+ const VolumeColorConfig: z.ZodType<VolumeColorConfig> = z.object({
173
+ colorBy: ColorBy,
174
+ transferFunction: ColorTransferFunction,
175
+ opacityFunction: OpacityFunction,
176
+ cvr: CVRConfig,
177
+ });
178
+
179
+ const ViewConfig = z.object({
180
+ window: WindowLevelConfig.optional(),
181
+ slice: SliceConfig.optional(),
182
+ camera: CameraConfig.optional(),
183
+ volumeColorConfig: VolumeColorConfig.optional(),
184
+ });
185
+
186
+ const ViewType: z.ZodType<ViewType> = z.union([
187
+ z.literal('2D'),
188
+ z.literal('3D'),
189
+ ]);
190
+
191
+ export type ViewConfig = z.infer<typeof ViewConfig>;
192
+
193
+ const View = z.object({
194
+ id: z.string(),
195
+ type: ViewType,
196
+ props: z.record(z.any()),
197
+ config: z.record(ViewConfig),
198
+ });
199
+
200
+ export type View = z.infer<typeof View>;
201
+
202
+ export const LabelMap = z.object({
203
+ id: z.string(),
204
+ parent: z.string(),
205
+ path: z.string(),
206
+ });
207
+
208
+ export type LabelMap = z.infer<typeof LabelMap>;
209
+
210
+ const LPSAxis: z.ZodType<LPSAxis> = z.union([
211
+ z.literal('Axial'),
212
+ z.literal('Sagittal'),
213
+ z.literal('Coronal'),
214
+ ]);
215
+
216
+ const InteractionStateNative = z.nativeEnum(InteractionState);
217
+
218
+ const Ruler: z.ZodType<Ruler> = z.object({
219
+ name: z.string(),
220
+ firstPoint: Vector3.nullable(),
221
+ secondPoint: Vector3.nullable(),
222
+ viewAxis: LPSAxis.nullable(),
223
+ slice: z.number().nullable(),
224
+ imageID: z.string(),
225
+ interactionState: InteractionStateNative,
226
+ color: z.string(),
227
+ });
228
+
229
+ const Crosshairs = z.object({
230
+ position: Vector3,
231
+ });
232
+
233
+ export type Crosshairs = z.infer<typeof Crosshairs>;
234
+
235
+ const ToolsEnumNative = z.nativeEnum(ToolsEnum);
236
+
237
+ const Paint = z.object({
238
+ activeLabelmapID: z.string().nullable(),
239
+ brushSize: z.number(),
240
+ brushValue: z.number(),
241
+ labelmapOpacity: z.number(),
242
+ });
243
+
244
+ const LPSCroppingPlanes: z.ZodType<LPSCroppingPlanes> = z.object({
245
+ Sagittal: z.tuple([z.number(), z.number()]),
246
+ Coronal: z.tuple([z.number(), z.number()]),
247
+ Axial: z.tuple([z.number(), z.number()]),
248
+ });
249
+
250
+ const Cropping = z.record(LPSCroppingPlanes);
251
+
252
+ const Tools = z.object({
253
+ rulers: Ruler.array(),
254
+ crosshairs: Crosshairs,
255
+ paint: Paint,
256
+ crop: Cropping,
257
+ current: ToolsEnumNative,
258
+ });
259
+
260
+ export type Tools = z.infer<typeof Tools>;
261
+
262
+ export const ManifestSchema = z.object({
263
+ version: z.string(),
264
+ dataSets: DataSet.array(),
265
+ labelMaps: LabelMap.array(),
266
+ tools: Tools,
267
+ views: View.array(),
268
+ primarySelection: z.string().optional(),
269
+ layout: Layout,
270
+ });
271
+
272
+ export type Manifest = z.infer<typeof ManifestSchema>;
273
+
274
+ export interface StateFile {
275
+ zip: JSZip;
276
+ manifest: Manifest;
277
+ }
@@ -0,0 +1,33 @@
1
+ import { StateFile, DataSetType } from './schema';
2
+ import { useFileStore } from '../../store/datasets-files';
3
+
4
+ export async function serializeData(
5
+ stateFile: StateFile,
6
+ dataIDs: string[],
7
+ dataType: DataSetType
8
+ ) {
9
+ const fileStore = useFileStore();
10
+ const { manifest } = stateFile;
11
+ const { zip } = stateFile;
12
+ const { dataSets } = manifest;
13
+
14
+ dataIDs.forEach((id) => {
15
+ const files = fileStore.getFiles(id);
16
+ if (files === null) {
17
+ throw new Error(`No files for dataID: ${id}`);
18
+ }
19
+
20
+ const dataPath = `data/${id}/`;
21
+ dataSets.push({
22
+ id,
23
+ path: dataPath,
24
+ type: dataType,
25
+ });
26
+
27
+ files.forEach((file: File) => {
28
+ const filePath = `data/${id}/${file.name}`;
29
+
30
+ zip.file(filePath, file);
31
+ });
32
+ });
33
+ }
@@ -0,0 +1,4 @@
1
+ export interface FileEntry {
2
+ file: File;
3
+ path: string;
4
+ }
@@ -0,0 +1,50 @@
1
+ import vtkSTLReader from '@kitware/vtk.js/IO/Geometry/STLReader';
2
+ import vtkXMLImageDataReader from '@kitware/vtk.js/IO/XML/XMLImageDataReader';
3
+ import vtkXMLPolyDataReader from '@kitware/vtk.js/IO/XML/XMLPolyDataReader';
4
+
5
+ import workerHandler from '@/src/utils/workerHandler';
6
+ import { readFile } from './common';
7
+
8
+ const Readers = {
9
+ stl: {
10
+ readerClass: vtkSTLReader,
11
+ asBinary: true,
12
+ },
13
+ vti: {
14
+ readerClass: vtkXMLImageDataReader,
15
+ asBinary: true,
16
+ },
17
+ vtp: {
18
+ readerClass: vtkXMLPolyDataReader,
19
+ asBinary: true,
20
+ },
21
+ };
22
+
23
+ export interface ReaderWorkerInput {
24
+ file: File;
25
+ readerName: keyof typeof Readers;
26
+ }
27
+
28
+ workerHandler.registerHandler(async (data: ReaderWorkerInput) => {
29
+ try {
30
+ const { file, readerName } = data;
31
+ if (!file) {
32
+ throw new Error('No file provided');
33
+ }
34
+ if (!(readerName in Readers)) {
35
+ throw new Error(`No reader found for ${file.name}`);
36
+ }
37
+
38
+ const { readerClass, asBinary } = Readers[readerName];
39
+ const ds = await readFile(file, readerClass, asBinary);
40
+ return {
41
+ status: 'success',
42
+ obj: ds.getState(),
43
+ };
44
+ } catch (error) {
45
+ return {
46
+ status: 'fail',
47
+ error: error as Error,
48
+ };
49
+ }
50
+ });
@@ -0,0 +1,58 @@
1
+ import vtk from '@kitware/vtk.js/vtk';
2
+
3
+ import PromiseWorker from '@/src/utils/promise-worker';
4
+ import vtkDataSet from '@kitware/vtk.js/Common/DataModel/DataSet';
5
+ import { vtkObject } from '@kitware/vtk.js/interfaces';
6
+ import { StateObject } from './common';
7
+
8
+ interface SuccessReadResult {
9
+ status: 'success';
10
+ obj: StateObject;
11
+ }
12
+
13
+ interface FailResult {
14
+ status: 'fail';
15
+ error: Error;
16
+ }
17
+
18
+ type ReadResult = SuccessReadResult | FailResult;
19
+
20
+ export const runAsyncVTKReader = (readerName: string) => async (file: File) => {
21
+ const worker = new PromiseWorker(
22
+ new Worker(new URL('./async.reader.worker.ts', import.meta.url))
23
+ );
24
+ const data = (await worker.postMessage({
25
+ file,
26
+ readerName,
27
+ })) as ReadResult;
28
+ if (data.status === 'success') {
29
+ return vtk(data.obj) as vtkObject;
30
+ }
31
+ throw data.error;
32
+ };
33
+
34
+ interface SuccessWriteResult {
35
+ status: 'success';
36
+ data: string;
37
+ }
38
+ type WriteResult = SuccessWriteResult | FailResult;
39
+
40
+ export const runAsyncVTKWriter =
41
+ (writerName: string) => async (dataSet: vtkDataSet) => {
42
+ const worker = new PromiseWorker(
43
+ new Worker(new URL('./async.writer.worker.ts', import.meta.url))
44
+ );
45
+ const result = (await worker.postMessage({
46
+ obj: dataSet.getState(),
47
+ writerName,
48
+ })) as WriteResult;
49
+ if (result.status === 'success') {
50
+ return result.data;
51
+ }
52
+ throw result.error;
53
+ };
54
+
55
+ export const stlReader = runAsyncVTKReader('stl');
56
+ export const vtiReader = runAsyncVTKReader('vti');
57
+ export const vtpReader = runAsyncVTKReader('vtp');
58
+ export const vtiWriter = runAsyncVTKWriter('vti');
@@ -0,0 +1,46 @@
1
+ import vtkXMLImageDataWriter from '@kitware/vtk.js/IO/XML/XMLImageDataWriter';
2
+ import vtkDataSet from '@kitware/vtk.js/Common/DataModel/DataSet';
3
+
4
+ import workerHandler from '@/src/utils/workerHandler';
5
+ import vtkLabelMap from '@/src/vtk/LabelMap';
6
+ import vtk from '@kitware/vtk.js/vtk';
7
+
8
+ import { writeData, StateObject } from './common';
9
+
10
+ const Writers = {
11
+ vti: {
12
+ writerClass: vtkXMLImageDataWriter,
13
+ },
14
+ };
15
+
16
+ export interface WorkerInput {
17
+ obj: StateObject;
18
+ writerName: keyof typeof Writers;
19
+ }
20
+
21
+ vtk.register('vtkLabelMap', vtkLabelMap.newInstance);
22
+
23
+ workerHandler.registerHandler(async (inputData: WorkerInput) => {
24
+ try {
25
+ const { obj, writerName } = inputData;
26
+ if (!obj) {
27
+ throw new Error('No data provided');
28
+ }
29
+ if (!(writerName in Writers)) {
30
+ throw new Error(`No writer found for ${writerName}`);
31
+ }
32
+
33
+ const { writerClass } = Writers[writerName];
34
+
35
+ const serialized = await writeData(writerClass, vtk(obj) as vtkDataSet);
36
+ return {
37
+ status: 'success',
38
+ data: serialized,
39
+ };
40
+ } catch (error) {
41
+ return {
42
+ status: 'fail',
43
+ error: error as Error,
44
+ };
45
+ }
46
+ });
@@ -0,0 +1,30 @@
1
+ import { vtkReader, vtkWriter, vtkClass } from '@/src/types/vtk-types';
2
+ import { readFileAsArrayBuffer, readFileAsUTF8Text } from '@/src/io';
3
+ import vtkDataSet from '@kitware/vtk.js/Common/DataModel/DataSet';
4
+
5
+ export async function readFile(
6
+ file: File,
7
+ vtkReaderClass: vtkClass,
8
+ asBinary = true
9
+ ) {
10
+ const reader: vtkReader = vtkReaderClass.newInstance() as vtkReader;
11
+ if (asBinary) {
12
+ const buffer = await readFileAsArrayBuffer(file);
13
+ reader.parseAsArrayBuffer(buffer);
14
+ } else {
15
+ const buffer = await readFileAsUTF8Text(file);
16
+ reader.parseAsText(buffer);
17
+ }
18
+ return reader.getOutputData();
19
+ }
20
+
21
+ export async function writeData(vtkWriterClass: vtkClass, data: vtkDataSet) {
22
+ const writer: vtkWriter = vtkWriterClass.newInstance() as vtkWriter;
23
+
24
+ return writer.write(data);
25
+ }
26
+
27
+ export interface StateObject {
28
+ vtkClass: string;
29
+ [attrName: string]: unknown;
30
+ }
package/src/io/zip.ts ADDED
@@ -0,0 +1,31 @@
1
+ import JSZip from 'jszip';
2
+ import { FileEntry } from './types';
3
+
4
+ export async function extractFilesFromZip(zipFile: File): Promise<FileEntry[]> {
5
+ const zip = await JSZip.loadAsync(zipFile);
6
+ const promises: Promise<File>[] = [];
7
+ const paths: string[] = [];
8
+ zip.forEach((relPath, file) => {
9
+ if (!file.dir) {
10
+ const splitPath = file.name.split('/');
11
+ const baseName = splitPath[splitPath.length - 1];
12
+ const path = [...splitPath.slice(0, splitPath.length - 1), ''].join('/');
13
+ const fileEntry = zip.file(file.name);
14
+ if (fileEntry) {
15
+ promises.push(
16
+ fileEntry.async('blob').then((blob) => new File([blob], baseName))
17
+ );
18
+ paths.push(path);
19
+ }
20
+ }
21
+ });
22
+
23
+ return Promise.all(promises).then((files) => {
24
+ return files.map((file, index) => {
25
+ return {
26
+ file,
27
+ path: paths[index],
28
+ };
29
+ });
30
+ });
31
+ }
package/src/main.js ADDED
@@ -0,0 +1,66 @@
1
+ import '@/public/global.css';
2
+ import '@kitware/vtk.js/Rendering/Profiles/All';
3
+ import 'vue-toastification/dist/index.css';
4
+
5
+ import Vue from 'vue';
6
+ import VueCompositionAPI from '@vue/composition-api';
7
+ import VueToast from 'vue-toastification';
8
+ import { createPinia, PiniaVuePlugin } from 'pinia';
9
+ import vtkProxyManager from '@kitware/vtk.js/Proxy/Core/ProxyManager';
10
+ import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
11
+ import vtkImageMapper from '@kitware/vtk.js/Rendering/Core/ImageMapper';
12
+
13
+ import App from './components/App.vue';
14
+ import vuetify from './plugins/vuetify';
15
+ import { DICOMIO } from './io/dicom';
16
+ import { FILE_READERS } from './io';
17
+ import { registerAllReaders } from './io/readers';
18
+ import proxyConfiguration from './vtk/proxy';
19
+ import { CorePiniaProviderPlugin } from './core/provider';
20
+ import ProxyWrapper from './core/proxies';
21
+ import { patchExitPointerLock } from './utils/hacks';
22
+
23
+ Vue.config.productionTip = false;
24
+
25
+ Vue.use(VueCompositionAPI);
26
+ Vue.use(VueToast);
27
+ Vue.use(PiniaVuePlugin);
28
+
29
+ // patches
30
+ patchExitPointerLock();
31
+
32
+ // Initialize global mapper topologies
33
+ // polys and lines in the front
34
+ vtkMapper.setResolveCoincidentTopologyToPolygonOffset();
35
+ vtkMapper.setResolveCoincidentTopologyPolygonOffsetParameters(-3, -3);
36
+ vtkMapper.setResolveCoincidentTopologyLineOffsetParameters(-3, -3);
37
+ // image poly in the back
38
+ vtkImageMapper.setResolveCoincidentTopologyToPolygonOffset();
39
+ vtkImageMapper.setResolveCoincidentTopologyPolygonOffsetParameters(1, 1);
40
+
41
+ registerAllReaders(FILE_READERS);
42
+
43
+ const proxyManager = vtkProxyManager.newInstance({ proxyConfiguration });
44
+
45
+ const dicomIO = new DICOMIO();
46
+ dicomIO.initialize();
47
+
48
+ const pinia = createPinia();
49
+ pinia.use(
50
+ CorePiniaProviderPlugin({
51
+ proxies: new ProxyWrapper(proxyManager),
52
+ dicomIO,
53
+ })
54
+ );
55
+
56
+ const app = new Vue({
57
+ vuetify,
58
+ proxyManager,
59
+ pinia,
60
+ provide: {
61
+ ProxyManager: proxyManager,
62
+ },
63
+ render: (h) => h(App),
64
+ });
65
+
66
+ app.$mount('#app');
package/src/main.ts ADDED
@@ -0,0 +1 @@
1
+ import './main.js';
@@ -0,0 +1,25 @@
1
+ import Vue from 'vue';
2
+ import Vuetify from 'vuetify/lib';
3
+ import { useLocalStorage } from '@vueuse/core';
4
+
5
+ import KitwareMark from '@/src/components/icons/KitwareLogoIcon.vue';
6
+
7
+ Vue.use(Vuetify);
8
+
9
+ const store = useLocalStorage('dark', true);
10
+
11
+ export default new Vuetify({
12
+ icons: {
13
+ values: {
14
+ kitwareMark: {
15
+ component: KitwareMark,
16
+ },
17
+ },
18
+ },
19
+ theme: {
20
+ dark: store.value,
21
+ },
22
+ breakpoint: {
23
+ mobileBreakpoint: 'sm',
24
+ },
25
+ });
@@ -0,0 +1,67 @@
1
+ declare module 'itk/IOTypes' {
2
+ const IOTypes = {
3
+ Text: 'Text',
4
+ Binary: 'Binary',
5
+ Image: 'Image',
6
+ Mesh: 'Mesh',
7
+ vtkPolyData: 'vtkPolyData',
8
+ } as const;
9
+
10
+ export default IOTypes;
11
+ }
12
+
13
+ declare module 'itk/runPipelineBrowser' {
14
+ async function runPipelineBrowser(
15
+ webWorker: Worker | null | boolean,
16
+ pipelinePath: string | URL,
17
+ args: string[],
18
+ outputs: PipelineOutput[] | null,
19
+ inputs: PipelineInput[] | null
20
+ ): Promise<any>;
21
+
22
+ export = runPipelineBrowser;
23
+ }
24
+
25
+ declare module 'itk/extensionToImageIO' {
26
+ export default {} as Map<string, string>;
27
+ }
28
+
29
+ declare module 'itk/readImageArrayBuffer' {
30
+ export default () => any;
31
+ }
32
+
33
+ declare module 'itk/ImageType' {
34
+ export interface ImageType {
35
+ dimension: number;
36
+ componentType: string;
37
+ pixelType: string;
38
+ components: number;
39
+ }
40
+
41
+ export default ImageType;
42
+ }
43
+
44
+ declare module 'itk/Image' {
45
+ import ImageType from 'itk/ImageType';
46
+ type TypedArray =
47
+ | Uint8Array
48
+ | Uint8ClampedArray
49
+ | Int8Array
50
+ | Uint16Array
51
+ | Int16Array
52
+ | Uint32Array
53
+ | Int32Array
54
+ | Float32Array
55
+ | Float64Array;
56
+
57
+ export interface Image {
58
+ imageType: ImageType;
59
+ origin: number[];
60
+ spacing: number[];
61
+ direction: number[];
62
+ size: number[];
63
+ data: TypedArray | null;
64
+ }
65
+
66
+ export default Image;
67
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.jpg' {
2
+ const url: string;
3
+ export default url;
4
+ }