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,215 @@
1
+ import vtkPiecewiseFunctionProxy from '@kitware/vtk.js/Proxy/Core/PiecewiseFunctionProxy';
2
+ import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
3
+ import vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
4
+ import vtkOpenGLRenderWindow from '@kitware/vtk.js/Rendering/OpenGL/RenderWindow';
5
+ import { Vector2, Vector3 } from '@kitware/vtk.js/types';
6
+ import { intersectDisplayWithPlane } from '@kitware/vtk.js/Widgets/Manipulators/PlaneManipulator';
7
+ import { OpacityFunction } from '../types/views';
8
+
9
+ export function computeWorldToDisplay(
10
+ xyz: Vector3,
11
+ renderer: vtkRenderer
12
+ ): Vector2 | null {
13
+ const view = renderer.getRenderWindow()?.getViews()?.[0];
14
+ if (view) {
15
+ const [x, y, z] = xyz;
16
+ return view.worldToDisplay(x, y, z, renderer);
17
+ }
18
+ return null;
19
+ }
20
+
21
+ export function computeDisplayToWorld(
22
+ xy: Vector2,
23
+ renderer: vtkRenderer
24
+ ): Vector3 | null {
25
+ const view = renderer.getRenderWindow()?.getViews()?.[0];
26
+ if (view) {
27
+ const [x, y] = xy;
28
+ return view.displayToWorld(x, y, 0, renderer);
29
+ }
30
+ return null;
31
+ }
32
+
33
+ export function normalizeMouseEventPosition(
34
+ ev: MouseEvent,
35
+ view: vtkOpenGLRenderWindow
36
+ ) {
37
+ // TODO fix typing in vtk.js
38
+ const canvas = (view as any).getCanvas() as HTMLCanvasElement | null;
39
+ if (!canvas) return null;
40
+
41
+ const bounds = canvas.getBoundingClientRect();
42
+ const scaleX = canvas.width / bounds.width;
43
+ const scaleY = canvas.height / bounds.height;
44
+ return [
45
+ scaleX * (ev.clientX - bounds.left),
46
+ scaleY * (bounds.height - ev.clientY + bounds.top),
47
+ ] as Vector2;
48
+ }
49
+
50
+ export function intersectMouseEventWithPlane(
51
+ ev: MouseEvent,
52
+ renderer: vtkRenderer,
53
+ origin: Vector3,
54
+ normal: Vector3
55
+ ) {
56
+ const view = renderer
57
+ .getRenderWindow()
58
+ ?.getViews()[0] as vtkOpenGLRenderWindow;
59
+ if (!view) return null;
60
+
61
+ const position = normalizeMouseEventPosition(ev, view);
62
+ if (!position) return null;
63
+
64
+ return intersectDisplayWithPlane(
65
+ position[0],
66
+ position[1],
67
+ origin,
68
+ normal,
69
+ renderer,
70
+ view
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Converts world coordinates to SVG-friendly coordinates.
76
+ *
77
+ * Assumes that the SVG layer is the same size as the renderer.
78
+ * TODO verify this is the case.
79
+ */
80
+ export function worldToSVG(xyz: Vector3, renderer: vtkRenderer) {
81
+ const coords = computeWorldToDisplay(xyz, renderer);
82
+ const view = renderer.getRenderWindow()?.getViews()?.[0];
83
+ if (coords && view) {
84
+ const [, height] = view.getViewportSize(renderer);
85
+ // convert from canvas space to svg space
86
+ return [
87
+ coords[0] / devicePixelRatio,
88
+ (height - coords[1]) / devicePixelRatio,
89
+ ] as Vector2;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ /**
95
+ * Gets the CSS coordinates for a vtk.js mouse event.
96
+ */
97
+ export function getCSSCoordinatesFromEvent(eventData: any) {
98
+ const { pokedRenderer }: { pokedRenderer: vtkRenderer } = eventData;
99
+ const view = pokedRenderer?.getRenderWindow()?.getViews()?.[0] as
100
+ | vtkOpenGLRenderWindow
101
+ | undefined;
102
+ const bbox = view?.getContainer()?.getBoundingClientRect();
103
+
104
+ if (!('position' in eventData) || !bbox) {
105
+ return null;
106
+ }
107
+
108
+ return [
109
+ bbox.left + eventData.position.x / devicePixelRatio,
110
+ bbox.top + bbox.height - eventData.position.y / devicePixelRatio,
111
+ ] as Vector2;
112
+ }
113
+
114
+ /**
115
+ * Shifts the opacity points from a preset.
116
+ */
117
+ export function getShiftedOpacityFromPreset(
118
+ presetName: string,
119
+ effectiveRange: [number, number],
120
+ shift: number
121
+ ) {
122
+ const preset = vtkColorMaps.getPresetByName(presetName);
123
+ if (preset.OpacityPoints) {
124
+ const OpacityPoints = preset.OpacityPoints as number[];
125
+ const points = [];
126
+ for (let i = 0; i < OpacityPoints.length; i += 2) {
127
+ points.push([OpacityPoints[i], OpacityPoints[i + 1]]);
128
+ }
129
+
130
+ const [xmin, xmax] = effectiveRange;
131
+ const width = xmax - xmin;
132
+ return points.map(([x, y]) => [(x - xmin) / width + shift, y]);
133
+ }
134
+ return null;
135
+ }
136
+
137
+ /**
138
+ * Retrieves an OpacityFunction from a preset.
139
+ */
140
+ export function getOpacityFunctionFromPreset(
141
+ presetName: string
142
+ ): Partial<OpacityFunction> {
143
+ const preset = vtkColorMaps.getPresetByName(presetName);
144
+
145
+ if (preset.OpacityPoints) {
146
+ return {
147
+ mode: vtkPiecewiseFunctionProxy.Mode.Points,
148
+ preset: presetName,
149
+ shift: 0,
150
+ };
151
+ }
152
+ return {
153
+ mode: vtkPiecewiseFunctionProxy.Mode.Gaussians,
154
+ // deep-copy necessary
155
+ gaussians: JSON.parse(
156
+ JSON.stringify(vtkPiecewiseFunctionProxy.Defaults.Gaussians)
157
+ ),
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Inflate an axis bounds defined as [min, max] by some delta.
163
+ *
164
+ * @param bounds Must be [number, number]. (Typed number[] for convenience.)
165
+ * @param delta
166
+ * @returns
167
+ */
168
+ export function inflateAxisBounds(bounds: number[], delta: number) {
169
+ return [bounds[0] + delta, bounds[1] + delta];
170
+ }
171
+
172
+ /**
173
+ * Retrieves the color function range, if any.
174
+ *
175
+ * Will only return the color function range if the preset
176
+ * has AbsoluteRange specified as true. For medical presets,
177
+ * the range is defined by the transfer function point range,
178
+ * rather than the dataset data range.
179
+ * @param presetName
180
+ * @returns
181
+ */
182
+ export function getColorFunctionRangeFromPreset(
183
+ presetName: string
184
+ ): [number, number] | null {
185
+ const preset = vtkColorMaps.getPresetByName(presetName);
186
+ if (!preset) return null;
187
+
188
+ const { AbsoluteRange, RGBPoints } = preset;
189
+ if (AbsoluteRange && RGBPoints) {
190
+ let min = Infinity;
191
+ let max = -Infinity;
192
+ for (let i = 0; i < RGBPoints.length; i += 4) {
193
+ min = Math.min(min, RGBPoints[i]);
194
+ max = Math.max(max, RGBPoints[i]);
195
+ }
196
+ return [min, max];
197
+ }
198
+ return null;
199
+ }
200
+
201
+ /**
202
+ * Retrieves the effective opacity mapping range, if any.
203
+ *
204
+ * Presets may specify an effective range for the scalar opacity
205
+ * mapping range.
206
+ * @param presetName
207
+ * @returns
208
+ */
209
+ export function getOpacityRangeFromPreset(presetName: string) {
210
+ const preset = vtkColorMaps.getPresetByName(presetName);
211
+ if (preset.EffectiveRange) {
212
+ return [...preset.EffectiveRange] as [number, number];
213
+ }
214
+ return null;
215
+ }
@@ -0,0 +1,42 @@
1
+ class WorkerHandler {
2
+ constructor() {
3
+ this.handler = null;
4
+ onmessage = this.preHandler.bind(this);
5
+ }
6
+
7
+ registerHandler(func) {
8
+ this.handler = func;
9
+ }
10
+
11
+ preHandler(ev) {
12
+ if (this.handler) {
13
+ const { id, message } = ev.data;
14
+
15
+ let transferables = [];
16
+ const setTransferables = (iter) => {
17
+ transferables = Array.from(iter);
18
+ };
19
+
20
+ try {
21
+ Promise.resolve(this.handler(message, setTransferables)).then(
22
+ (result) => {
23
+ const msg = {
24
+ id,
25
+ message: result,
26
+ };
27
+ postMessage(msg, transferables);
28
+ }
29
+ );
30
+ } catch (error) {
31
+ const msg = {
32
+ id,
33
+ error: error.message,
34
+ };
35
+ postMessage(msg);
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ // To be used inside the worker
42
+ export default new WorkerHandler();
@@ -0,0 +1,72 @@
1
+ import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
2
+ import MedicalPresets from '@/src/vtk/MedicalColorPresets.json';
3
+
4
+ function registerPresets(presets: typeof MedicalPresets) {
5
+ for (let i = 0; i < presets.length; i += 1) {
6
+ vtkColorMaps.addPreset(presets[i]);
7
+ }
8
+ }
9
+
10
+ registerPresets(MedicalPresets);
11
+
12
+ /* prettier-ignore */
13
+ const GroupedPresets = [
14
+ {
15
+ group: 'Other',
16
+ presets: [
17
+ 'Cool to Warm',
18
+ ],
19
+ },
20
+ {
21
+ group: 'CT',
22
+ presets: [
23
+ 'CT-AAA',
24
+ 'CT-AAA2',
25
+ 'CT-Bone',
26
+ 'CT-Bones',
27
+ 'CT-Cardiac',
28
+ 'CT-Cardiac2',
29
+ 'CT-Cardiac3',
30
+ 'CT-Chest-Contrast-Enhanced',
31
+ 'CT-Chest-Vessels',
32
+ 'CT-Coronary-Arteries',
33
+ 'CT-Coronary-Arteries-2',
34
+ 'CT-Coronary-Arteries-3',
35
+ 'CT-Cropped-Volume-Bone',
36
+ 'CT-Fat',
37
+ 'CT-Liver-Vasculature',
38
+ 'CT-Lung',
39
+ 'CT-MIP',
40
+ 'CT-Muscle',
41
+ 'CT-Pulmonary-Arteries',
42
+ 'CT-Soft-Tissue',
43
+ 'CT-Air',
44
+ 'CT-X-ray',
45
+ ],
46
+ },
47
+ {
48
+ group: 'MR',
49
+ presets: [
50
+ 'MR-Default',
51
+ 'MR-Angio',
52
+ 'MR-MIP',
53
+ 'MR-T2-Brain',
54
+ ],
55
+ },
56
+ {
57
+ group: 'DTI',
58
+ presets: [
59
+ 'DTI-FA-Brain',
60
+ ],
61
+ },
62
+ {
63
+ group: 'US',
64
+ presets: [
65
+ 'US-Fetal',
66
+ ],
67
+ },
68
+ ];
69
+
70
+ export const DEFAULT_PRESET = 'Cool to Warm';
71
+ export const PresetNameList = GroupedPresets.flatMap((group) => group.presets);
72
+ export default GroupedPresets;
@@ -0,0 +1,112 @@
1
+ import macro from '@kitware/vtk.js/macro';
2
+ import { Bounds } from '@kitware/vtk.js/types';
3
+ import { vec3 } from 'gl-matrix';
4
+
5
+ function clampPointToBounds(bounds: Bounds, point: vec3) {
6
+ return point.map((p, i) =>
7
+ Math.max(bounds[i * 2], Math.min(bounds[i * 2 + 1], p))
8
+ ) as vec3;
9
+ }
10
+
11
+ export default function widgetBehavior(publicAPI: any, model: any) {
12
+ model.classHierarchy.push('vtkCrosshairsWidgetProp');
13
+ let isDragging = false;
14
+
15
+ // support setting per-view widget manipulators
16
+ macro.setGet(publicAPI, model, ['manipulator']);
17
+
18
+ // --------------------------------------------------------------------------
19
+ // Interactor events
20
+ // --------------------------------------------------------------------------
21
+
22
+ function ignoreKey(e: any) {
23
+ return e.altKey || e.controlKey || e.shiftKey;
24
+ }
25
+
26
+ // --------------------------------------------------------------------------
27
+ // Left press: Select handle to drag
28
+ // --------------------------------------------------------------------------
29
+
30
+ publicAPI.handleLeftButtonPress = (e: any) => {
31
+ if (!model.pickable || ignoreKey(e)) {
32
+ return macro.VOID;
33
+ }
34
+
35
+ model.widgetState.setPlaced(true);
36
+ isDragging = true;
37
+ model._interactor.requestAnimation(publicAPI);
38
+ model._apiSpecificRenderWindow.setCursor('crosshairs');
39
+ publicAPI.invokeStartInteractionEvent();
40
+ publicAPI.handleMouseMove(e);
41
+ return macro.EVENT_ABORT;
42
+ };
43
+
44
+ // --------------------------------------------------------------------------
45
+ // Mouse move: Drag selected handle / Handle follow the mouse
46
+ // --------------------------------------------------------------------------
47
+
48
+ publicAPI.handleMouseMove = (callData: any) => {
49
+ // technically need to call requestAnimation during
50
+ // the initial phase of placing the crosshairs,
51
+ // but that's not needed since no VTK object
52
+ // is actually being rendered.
53
+
54
+ if (
55
+ (!model.widgetState.getPlaced() || isDragging) &&
56
+ model.pickable &&
57
+ model.manipulator &&
58
+ !ignoreKey(callData)
59
+ ) {
60
+ const worldCoordsOfPointer = model.manipulator.handleEvent(
61
+ callData,
62
+ model._apiSpecificRenderWindow
63
+ );
64
+
65
+ const handle = model.widgetState.getHandle();
66
+ const worldToIndex = model.widgetState.getWorldToIndex();
67
+ const indexToWorld = model.widgetState.getIndexToWorld();
68
+ if (worldToIndex.length && indexToWorld.length) {
69
+ const indexCoordsOfPointer = vec3.create();
70
+ const worldOrigin = vec3.create();
71
+ const bounds = handle.getBounds();
72
+
73
+ vec3.transformMat4(
74
+ indexCoordsOfPointer,
75
+ worldCoordsOfPointer,
76
+ worldToIndex
77
+ );
78
+ const indexOrigin = clampPointToBounds(bounds, indexCoordsOfPointer);
79
+ vec3.transformMat4(worldOrigin, indexOrigin, indexToWorld);
80
+
81
+ handle.setOrigin(worldOrigin);
82
+ publicAPI.invokeInteractionEvent();
83
+ return macro.EVENT_ABORT;
84
+ }
85
+ }
86
+
87
+ return macro.VOID;
88
+ };
89
+
90
+ // --------------------------------------------------------------------------
91
+ // Left release: Finish drag / Create new handle
92
+ // --------------------------------------------------------------------------
93
+
94
+ publicAPI.handleLeftButtonRelease = () => {
95
+ if (isDragging && model.pickable) {
96
+ model._interactor.cancelAnimation(publicAPI);
97
+ model._apiSpecificRenderWindow.setCursor('default');
98
+ publicAPI.invokeEndInteractionEvent();
99
+ }
100
+ isDragging = false;
101
+ };
102
+
103
+ // --------------------------------------------------------------------------
104
+ // Focus API - modeHandle follow mouse when widget has focus
105
+ // --------------------------------------------------------------------------
106
+
107
+ publicAPI.grabFocus = () => {};
108
+
109
+ // --------------------------------------------------------------------------
110
+
111
+ publicAPI.loseFocus = () => {};
112
+ }
@@ -0,0 +1,22 @@
1
+ import vtkAbstractWidget from '@kitware/vtk.js/Widgets/Core/AbstractWidget';
2
+ import vtkAbstractWidgetFactory from '@kitware/vtk.js/Widgets/Core/AbstractWidgetFactory';
3
+ import vtkPlaneManipulator from '@kitware/vtk.js/Widgets/Manipulators/PlaneManipulator';
4
+ import { mat4, vec3 } from 'gl-matrix';
5
+ import { CrosshairsWidgetState } from './state';
6
+
7
+ export interface vtkCrosshairsViewWidget extends vtkAbstractWidget {
8
+ setManipulator(manipulator: vtkPlaneManipulator): boolean;
9
+ getManipulator(): vtkPlaneManipulator;
10
+ }
11
+
12
+ export interface vtkCrosshairsWidget extends vtkAbstractWidgetFactory {
13
+ getWidgetState(): CrosshairsWidgetState;
14
+ getManipulator(): vtkPlaneManipulator;
15
+ }
16
+
17
+ export function newInstance(): vtkCrosshairsWidget;
18
+
19
+ export declare const vtkCrosshairsWidget: {
20
+ newInstance: typeof newInstance;
21
+ };
22
+ export default vtkCrosshairsWidget;
@@ -0,0 +1,73 @@
1
+ import macro from '@kitware/vtk.js/macro';
2
+ import vtkAbstractWidgetFactory from '@kitware/vtk.js/Widgets/Core/AbstractWidgetFactory';
3
+ import vtkPlanePointManipulator from '@kitware/vtk.js/Widgets/Manipulators/PlaneManipulator';
4
+
5
+ import { ViewTypes } from '@kitware/vtk.js/Widgets/Core/WidgetManager/Constants';
6
+
7
+ import stateGenerator from './state';
8
+ import widgetBehavior from './behavior';
9
+
10
+ // ----------------------------------------------------------------------------
11
+ // Factory
12
+ // ----------------------------------------------------------------------------
13
+
14
+ function vtkCrosshairsWidget(publicAPI, model) {
15
+ model.classHierarchy.push('vtkCrosshairsWidget');
16
+
17
+ // --- Widget Requirement ---------------------------------------------------
18
+
19
+ model.behavior = widgetBehavior;
20
+ model.widgetState = stateGenerator();
21
+
22
+ publicAPI.getRepresentationsForViewType = (viewType) => {
23
+ switch (viewType) {
24
+ case ViewTypes.DEFAULT:
25
+ case ViewTypes.GEOMETRY:
26
+ case ViewTypes.SLICE:
27
+ case ViewTypes.VOLUME:
28
+ default:
29
+ return [];
30
+ }
31
+ };
32
+
33
+ // --------------------------------------------------------------------------
34
+ // initialization
35
+ // --------------------------------------------------------------------------
36
+
37
+ model.widgetState.onBoundsChange((bounds) => {
38
+ const center = [
39
+ (bounds[0] + bounds[1]) * 0.5,
40
+ (bounds[2] + bounds[3]) * 0.5,
41
+ (bounds[4] + bounds[5]) * 0.5,
42
+ ];
43
+ model.widgetState.getHandle().setOrigin(center);
44
+ });
45
+
46
+ // Default manipulator
47
+ model.manipulator = vtkPlanePointManipulator.newInstance();
48
+ }
49
+
50
+ // ----------------------------------------------------------------------------
51
+
52
+ const DEFAULT_VALUES = {
53
+ // manipulator: null,
54
+ };
55
+
56
+ // ----------------------------------------------------------------------------
57
+
58
+ export function extend(publicAPI, model, initialValues = {}) {
59
+ Object.assign(model, DEFAULT_VALUES, initialValues);
60
+
61
+ vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues);
62
+ macro.setGet(publicAPI, model, ['manipulator']);
63
+
64
+ vtkCrosshairsWidget(publicAPI, model);
65
+ }
66
+
67
+ // ----------------------------------------------------------------------------
68
+
69
+ export const newInstance = macro.newInstance(extend, 'vtkCrosshairsWidget');
70
+
71
+ // ----------------------------------------------------------------------------
72
+
73
+ export default { newInstance, extend };
@@ -0,0 +1,51 @@
1
+ import { Bounds, Vector3 } from '@kitware/vtk.js/types';
2
+ import vtkStateBuilder from '@kitware/vtk.js/Widgets/Core/StateBuilder';
3
+ import vtkWidgetState from '@kitware/vtk.js/Widgets/Core/WidgetState';
4
+ import { mat4 } from 'gl-matrix';
5
+
6
+ export interface CrosshairsHandleWidgetState extends vtkWidgetState {
7
+ setOrigin(origin: Vector3 | null): boolean;
8
+ getOrigin(): Vector3 | null;
9
+ setScale1(scale: number): boolean;
10
+ getScale1(): number;
11
+ setVisible(visible: boolean): boolean;
12
+ getVisible(): boolean;
13
+ setBounds(bounds: Bounds): boolean;
14
+ getBounds(): Bounds;
15
+ }
16
+
17
+ export interface CrosshairsWidgetState extends vtkWidgetState {
18
+ setPlaced(placed: boolean): boolean;
19
+ getPlaced(): boolean;
20
+ setIndexToWorld(indexToWorld: mat4): boolean;
21
+ getIndexToWorld(): mat4;
22
+ setWorldToIndex(worldToIndex: mat4): boolean;
23
+ getWorldToIndex(): mat4;
24
+ getHandle(): CrosshairsHandleWidgetState;
25
+ }
26
+
27
+ export default function generateState() {
28
+ return vtkStateBuilder
29
+ .createBuilder()
30
+ .addField({
31
+ name: 'placed',
32
+ initialValue: false,
33
+ })
34
+ .addField({
35
+ name: 'indexToWorld',
36
+ initialValue: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
37
+ })
38
+ .addField({
39
+ name: 'worldToIndex',
40
+ initialValue: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
41
+ })
42
+ .addStateFromMixin({
43
+ labels: ['handle'],
44
+ mixins: ['origin', 'bounds'],
45
+ name: 'handle',
46
+ initialValues: {
47
+ origin: null,
48
+ },
49
+ })
50
+ .build();
51
+ }
@@ -0,0 +1,9 @@
1
+ import vtkSliceRepresentationProxy from '@kitware/vtk.js/Proxy/Core/SliceRepresentationProxy';
2
+
3
+ export interface vtkIJKSliceRepresentationProxy
4
+ extends vtkSliceRepresentationProxy {
5
+ setOpacity(opacity: number): boolean;
6
+ getOpacity(): number;
7
+ }
8
+
9
+ export default vtkIJKSliceRepresentationProxy;
@@ -0,0 +1,59 @@
1
+ import macro from '@kitware/vtk.js/macro';
2
+
3
+ import vtkSliceRepresentationProxy from '@kitware/vtk.js/Proxy/Representations/SliceRepresentationProxy';
4
+
5
+ const SLICE_MODE_MAP = {
6
+ X: 'I',
7
+ Y: 'J',
8
+ Z: 'K',
9
+ I: 'I',
10
+ J: 'J',
11
+ K: 'K',
12
+ };
13
+
14
+ function vtkIJKSliceRepresentationProxy(publicAPI, model) {
15
+ model.classHierarchy.push('vtkIJKSliceRepresentationProxy');
16
+
17
+ const superClass = { ...publicAPI };
18
+
19
+ // don't set colors on slices
20
+ publicAPI.setColorBy = () => {};
21
+
22
+ // pretend XYZ slicing is actually just IJK.
23
+ publicAPI.setSlicingMode = (modeString) => {
24
+ return superClass.setSlicingMode(SLICE_MODE_MAP[modeString]);
25
+ };
26
+ }
27
+
28
+ // ----------------------------------------------------------------------------
29
+ // Object factory
30
+ // ----------------------------------------------------------------------------
31
+
32
+ const DEFAULT_VALUES = {};
33
+
34
+ // ----------------------------------------------------------------------------
35
+
36
+ export function extend(publicAPI, model, initialValues = {}) {
37
+ Object.assign(model, DEFAULT_VALUES, initialValues);
38
+
39
+ // Object methods
40
+ vtkSliceRepresentationProxy.extend(publicAPI, model);
41
+
42
+ macro.proxyPropertyMapping(publicAPI, model, {
43
+ opacity: { modelKey: 'property', property: 'opacity' },
44
+ });
45
+
46
+ // Object specific methods
47
+ vtkIJKSliceRepresentationProxy(publicAPI, model);
48
+ }
49
+
50
+ // ----------------------------------------------------------------------------
51
+
52
+ export const newInstance = macro.newInstance(
53
+ extend,
54
+ 'vtkIJKSliceRepresentationProxy'
55
+ );
56
+
57
+ // ----------------------------------------------------------------------------
58
+
59
+ export default { newInstance, extend };
@@ -0,0 +1,18 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { vtkView2DProxy } from '@kitware/vtk.js/Proxy/Core/View2DProxy';
3
+ import { ViewProxyCustomizations } from '@src/vtk/LPSView3DProxy';
4
+
5
+ export interface vtkLPSView2DProxy
6
+ extends vtkView2DProxy,
7
+ ViewProxyCustomizations {
8
+ resizeToFit(lookAxis: Vector3, viewUpAxis: Vector3, worldDims: Vector3);
9
+ resetCamera(boundsToUse?: number[]);
10
+ /**
11
+ * @param mode One of IJKXYZ
12
+ */
13
+ setSlicingMode(mode: string);
14
+ }
15
+
16
+ // TODO extend, newInstance...
17
+ export declare const vtkLPSView2DProxy: {};
18
+ export default vtkLPSView2DProxy;