@3cr/viewer-browser 0.0.200 → 0.0.246

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 (383) hide show
  1. package/.circleci/config.yml +46 -22
  2. package/.prettierrc +6 -0
  3. package/components.d.ts +27 -10
  4. package/dist/Viewer3CR.js +36 -45
  5. package/dist/Viewer3CR.mjs +26893 -21667
  6. package/dist/Viewer3CR.umd.js +36 -45
  7. package/dist/android-chrome-192x192.png +0 -0
  8. package/dist/android-chrome-512x512.png +0 -0
  9. package/dist/apple-touch-icon.png +0 -0
  10. package/dist/browserconfig.xml +9 -0
  11. package/dist/favicon-16x16.png +0 -0
  12. package/dist/favicon-32x32.png +0 -0
  13. package/dist/favicon.ico +0 -0
  14. package/dist/mstile-144x144.png +0 -0
  15. package/dist/mstile-150x150.png +0 -0
  16. package/dist/mstile-310x150.png +0 -0
  17. package/dist/mstile-310x310.png +0 -0
  18. package/dist/mstile-70x70.png +0 -0
  19. package/dist/safari-pinned-tab.svg +50 -0
  20. package/dist/site.webmanifest +19 -0
  21. package/index.html +34 -41
  22. package/package.json +24 -20
  23. package/playground/android-chrome-192x192.png +0 -0
  24. package/playground/android-chrome-512x512.png +0 -0
  25. package/playground/apple-touch-icon.png +0 -0
  26. package/playground/browserconfig.xml +9 -0
  27. package/playground/favicon-16x16.png +0 -0
  28. package/playground/favicon-32x32.png +0 -0
  29. package/playground/favicon.ico +0 -0
  30. package/playground/index.html +30 -41
  31. package/playground/mstile-144x144.png +0 -0
  32. package/playground/mstile-150x150.png +0 -0
  33. package/playground/mstile-310x150.png +0 -0
  34. package/playground/mstile-310x310.png +0 -0
  35. package/playground/mstile-70x70.png +0 -0
  36. package/playground/safari-pinned-tab.svg +50 -0
  37. package/playground/site.webmanifest +19 -0
  38. package/playground/sw.js +15 -1
  39. package/public/android-chrome-192x192.png +0 -0
  40. package/public/android-chrome-512x512.png +0 -0
  41. package/public/apple-touch-icon.png +0 -0
  42. package/public/browserconfig.xml +9 -0
  43. package/public/favicon-16x16.png +0 -0
  44. package/public/favicon-32x32.png +0 -0
  45. package/public/favicon.ico +0 -0
  46. package/public/mstile-144x144.png +0 -0
  47. package/public/mstile-150x150.png +0 -0
  48. package/public/mstile-310x150.png +0 -0
  49. package/public/mstile-310x310.png +0 -0
  50. package/public/mstile-70x70.png +0 -0
  51. package/public/safari-pinned-tab.svg +50 -0
  52. package/public/site.webmanifest +19 -0
  53. package/src/App.vue +55 -50
  54. package/src/__tests__/app.spec.ts +7 -8
  55. package/{__tests__/index.spec.ts → src/__tests__/main.spec.ts} +5 -6
  56. package/src/assets/logos/3dicom/white-mini.svg +20 -0
  57. package/src/assets/logos/3dicom/white.svg +30 -0
  58. package/src/assets/styles.scss +18 -4
  59. package/src/components/demo/DemoPatientModal.vue +12 -22
  60. package/src/components/demo/__tests__/DemoModal.spec.ts +9 -8
  61. package/src/components/demo/__tests__/DemoPatientModal.spec.ts +11 -10
  62. package/src/components/demo/__tests__/options.spec.ts +1 -1
  63. package/src/components/demo/licence/DemoLicenceInfoModal.vue +11 -29
  64. package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +11 -10
  65. package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +21 -11
  66. package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +10 -10
  67. package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +14 -13
  68. package/src/components/demo/options.ts +74 -76
  69. package/src/components/demo/patient/DemoPatientInfoModal.vue +11 -29
  70. package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +11 -10
  71. package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +12 -11
  72. package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +10 -10
  73. package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +14 -13
  74. package/src/components/modal/ActionRail.vue +7 -11
  75. package/src/components/modal/AskAI.vue +35 -50
  76. package/src/components/modal/CloseViewerModal.vue +22 -57
  77. package/src/components/modal/MftpWebGL3DRModal.vue +230 -635
  78. package/src/components/modal/ViewerActionRail.vue +44 -39
  79. package/src/components/modal/ViewerNavigationDrawer.vue +10 -8
  80. package/src/components/modal/ViewerNavigationDrawerContent.vue +91 -49
  81. package/src/components/modal/ViewerNavigationDrawerFooter.vue +83 -63
  82. package/src/components/modal/ViewerNavigationDrawerHeader.vue +14 -44
  83. package/src/components/modal/ViewerScanView.vue +114 -0
  84. package/src/components/modal/WebGL3DR.vue +91 -0
  85. package/src/components/modal/__tests__/ActionRail.spec.ts +10 -0
  86. package/src/components/modal/__tests__/AskAI.spec.ts +33 -0
  87. package/src/components/modal/__tests__/CloseViewerModal.spec.ts +24 -14
  88. package/src/components/modal/__tests__/MftpWebGL3DRModal.spec.ts +147 -176
  89. package/src/components/modal/__tests__/ViewerActionRail.spec.ts +11 -0
  90. package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +10 -12
  91. package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +4 -22
  92. package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +35 -32
  93. package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +14 -11
  94. package/src/components/modal/__tests__/ViewerScanView.spec.ts +60 -0
  95. package/src/components/modal/__tests__/WebGL3DR.spec.ts +57 -0
  96. package/src/components/modal/actions/Flip3dAction.vue +1 -1
  97. package/src/components/modal/actions/FlipHorizontalAction.vue +5 -10
  98. package/src/components/modal/actions/FlipVerticalAction.vue +5 -10
  99. package/src/components/modal/actions/FullscreenAction.vue +6 -11
  100. package/src/components/modal/actions/NavigationCubeAction.vue +7 -14
  101. package/src/components/modal/actions/PanAction.vue +4 -4
  102. package/src/components/modal/actions/ResetViewAction.vue +1 -1
  103. package/src/components/modal/actions/Rotate2dAction.vue +8 -20
  104. package/src/components/modal/actions/Slice3dAction.vue +10 -32
  105. package/src/components/modal/actions/ZoomAction.vue +1 -1
  106. package/src/components/modal/actions/__tests__/Action.spec.ts +8 -9
  107. package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +6 -8
  108. package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +6 -7
  109. package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +6 -7
  110. package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +9 -10
  111. package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +11 -11
  112. package/src/components/modal/actions/__tests__/PanAction.spec.ts +9 -10
  113. package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +7 -7
  114. package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +6 -8
  115. package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +23 -5
  116. package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +8 -10
  117. package/src/components/modal/buttons/AutoAnnotateBtn.vue +197 -0
  118. package/src/components/modal/buttons/__tests__/AutoAnnotateBtn.spec.ts +28 -0
  119. package/src/components/modal/menus/FileMenu.vue +73 -0
  120. package/src/components/modal/menus/SettingsMenu.vue +106 -0
  121. package/src/components/modal/menus/__tests__/FileMenu.spec.ts +110 -0
  122. package/src/components/modal/menus/__tests__/SettingsMenu.spec.ts +71 -0
  123. package/src/components/navigation/mcad/McadGlobalActions.vue +20 -0
  124. package/src/components/navigation/mcad/McadGlobalOpacitySlider.vue +103 -0
  125. package/src/components/navigation/mcad/McadGlobalScanViewBtn.vue +28 -0
  126. package/src/components/navigation/mcad/McadGlobalVisibilityBtn.vue +38 -0
  127. package/src/components/shared/DoubleSliderSelector.vue +142 -0
  128. package/src/components/{loading → shared}/LoadingSpinner.vue +27 -36
  129. package/src/components/shared/UpdateSnackbar.vue +72 -0
  130. package/src/components/{selectors → shared}/ValueSelector.vue +9 -12
  131. package/src/components/shared/__tests__/DoubleSliderSelector.spec.ts +83 -0
  132. package/src/components/shared/__tests__/LoadingSpinner.spec.ts +9 -0
  133. package/src/components/shared/__tests__/UpdateSnackbar.spec.ts +116 -0
  134. package/src/components/shared/__tests__/ValueSelector.spec.ts +39 -0
  135. package/src/components/shared/__tests__/VerticalSliderSelector.spec.ts +50 -0
  136. package/src/components/views/AnnotationTreeView.vue +236 -0
  137. package/src/components/{modal/ViewerDisplaySettings.vue → views/DisplaySettings.vue} +7 -15
  138. package/src/components/views/MarkupTreeView.vue +201 -0
  139. package/src/components/views/McadObjectTreeView.vue +129 -0
  140. package/src/components/views/__tests__/AnnotationTreeView.spec.ts +62 -0
  141. package/src/components/{modal/__tests__/ViewerDisplaySettings.spec.ts → views/__tests__/DisplaySettings.spec.ts} +24 -20
  142. package/src/components/views/__tests__/MarkupTreeView.spec.ts +41 -0
  143. package/src/components/views/__tests__/McadObjectTreeView.spec.ts +47 -0
  144. package/src/components/views/modals/DataOverlayGeneralModal.vue +71 -0
  145. package/src/components/views/modals/DataOverlayMarkupModal.vue +60 -0
  146. package/src/components/views/modals/DataOverlayModal.vue +88 -0
  147. package/src/components/views/shared/MaskIcon.vue +44 -0
  148. package/src/components/views/shared/ObjectColor.vue +31 -0
  149. package/src/components/views/shared/ObjectLabel.vue +30 -0
  150. package/src/components/views/shared/Opacity.vue +65 -0
  151. package/src/components/views/shared/VisibilityBtn.vue +25 -0
  152. package/src/components/views/shared/__tests__/MaskIcon.spec.ts +10 -0
  153. package/src/components/views/shared/__tests__/ObjectColor.spec.ts +10 -0
  154. package/src/components/views/shared/__tests__/ObjectLabel.spec.ts +10 -0
  155. package/src/components/views/shared/__tests__/Opacity.spec.ts +24 -0
  156. package/src/components/views/shared/__tests__/VisibilityBtn.spec.ts +11 -0
  157. package/src/components/views/types/annotation-tree-view-item-child-action.ts +6 -0
  158. package/src/components/views/types/annotation-tree-view-item-child.ts +9 -0
  159. package/src/components/views/types/annotation-tree-view-item.ts +13 -0
  160. package/src/components/views/types/markup-tree-view-item-child.ts +6 -0
  161. package/src/components/views/types/markup-tree-view-item.ts +14 -0
  162. package/src/components/views/types/mcad-object-tree-view-item.ts +13 -0
  163. package/src/composables/__tests__/useAnnotations.spec.ts +35 -0
  164. package/src/composables/__tests__/useEventListener.spec.ts +32 -0
  165. package/src/composables/__tests__/useIntroJs.spec.ts +51 -0
  166. package/src/composables/__tests__/useMarkups.spec.ts +39 -0
  167. package/src/composables/__tests__/useMcadObjects.spec.ts +36 -0
  168. package/src/composables/__tests__/useMouse.spec.ts +20 -0
  169. package/src/{components/modal/composables → composables}/__tests__/useNavigationCubeObserver.spec.ts +17 -12
  170. package/src/{dataLayer → composables}/__tests__/useViewer3cr.spec.ts +1 -2
  171. package/src/composables/useAnnotations.ts +37 -0
  172. package/src/composables/useDebounce.ts +11 -0
  173. package/src/composables/useEventListener.ts +25 -0
  174. package/src/composables/useIntroJs.ts +142 -0
  175. package/src/composables/useMarkups.ts +41 -0
  176. package/src/composables/useMcadObjects.ts +32 -0
  177. package/src/composables/useMouse.ts +14 -0
  178. package/src/{components/modal/composables → composables}/useNavigationCubeObserver.ts +35 -25
  179. package/src/composables/useScanMovement.ts +25 -0
  180. package/src/composables/useScanSliders.ts +36 -0
  181. package/src/composables/useVersion3cr.ts +7 -0
  182. package/src/composables/useViewer3cr.ts +7 -0
  183. package/src/composables/useViewerOptions.ts +36 -0
  184. package/src/{dataLayer → functions}/__tests__/clamp.spec.ts +1 -2
  185. package/src/functions/__tests__/layoutOverlayStyle.spec.ts +325 -0
  186. package/src/{helpers/__tests__/model-helper.spec.ts → functions/__tests__/modelHelper.spec.ts} +77 -40
  187. package/src/{notifications → functions}/__tests__/notification.spec.ts +19 -29
  188. package/src/functions/__tests__/parseAction.spec.ts +9 -0
  189. package/src/functions/__tests__/parseCallToAction.spec.ts +11 -0
  190. package/src/functions/__tests__/parseDataOverlay.spec.ts +15 -0
  191. package/src/functions/__tests__/parseDataOverlayData.spec.ts +9 -0
  192. package/src/functions/__tests__/parseDataOverlayEvent.spec.ts +10 -0
  193. package/src/functions/__tests__/parseMcadEvent.spec.ts +10 -0
  194. package/src/functions/guards/isDataOverlayAngle.ts +6 -0
  195. package/src/functions/guards/isDataOverlayLength.ts +6 -0
  196. package/src/functions/guards/isDataOverlayPolygon.ts +6 -0
  197. package/src/functions/layoutOverlayStyle.ts +84 -0
  198. package/src/{helpers → functions}/modelHelper.ts +81 -18
  199. package/src/functions/notification.ts +82 -0
  200. package/src/functions/parseAction.ts +9 -0
  201. package/src/functions/parseCallToAction.ts +9 -0
  202. package/src/functions/parseDataOverlay.ts +10 -0
  203. package/src/functions/parseDataOverlayData.ts +10 -0
  204. package/src/functions/parseDataOverlayEvent.ts +17 -0
  205. package/src/functions/parseMcadEvent.ts +10 -0
  206. package/src/functions/rgbaToCss.ts +13 -0
  207. package/src/main.ts +57 -9
  208. package/src/{dataLayer → models}/__tests__/eventHandlers.spec.ts +2 -3
  209. package/src/models/__tests__/loadViewerOptions.spec.ts +72 -0
  210. package/src/models/__tests__/loadViewerPayload.spec.ts +10 -0
  211. package/src/models/__tests__/scanState.spec.ts +190 -0
  212. package/src/models/callbacks.ts +4 -0
  213. package/src/models/loadViewerOptions.ts +79 -0
  214. package/src/models/loadViewerPayload.ts +17 -0
  215. package/src/{dataLayer → models}/scanState.ts +63 -74
  216. package/src/plugins/__tests__/usePlugins.spec.ts +12 -0
  217. package/src/plugins/__tests__/vuetify.spec.ts +3 -4
  218. package/src/plugins/usePlugins.ts +8 -0
  219. package/src/plugins/vuetify.ts +31 -65
  220. package/src/services/{gpt/__tests__ → __tests__}/gpt.service.spec.ts +2 -2
  221. package/src/services/__tests__/service-worker.service.spec.ts +32 -0
  222. package/src/{dataLayer/__tests__/viewer3cr.spec.ts → services/__tests__/viewer-3cr.service.spec.ts} +97 -36
  223. package/src/services/gpt.service.ts +17 -0
  224. package/src/services/service-worker.service.ts +16 -0
  225. package/src/{dataLayer/viewer3cr.ts → services/viewer-3cr.service.ts} +281 -150
  226. package/src/tools/data-overlay.tool.ts +71 -0
  227. package/src/types/action.ts +13 -0
  228. package/src/types/call-to-action.ts +9 -0
  229. package/src/types/colour.ts +6 -0
  230. package/src/types/data-overlay-angle.ts +16 -0
  231. package/src/types/data-overlay-annotation.ts +18 -0
  232. package/src/types/data-overlay-data.ts +15 -0
  233. package/src/types/data-overlay-event.ts +5 -0
  234. package/src/types/data-overlay-info.ts +18 -0
  235. package/src/types/data-overlay-interaction.ts +15 -0
  236. package/src/types/data-overlay-length.ts +16 -0
  237. package/src/types/data-overlay-markup.ts +7 -0
  238. package/src/types/data-overlay-mcad.ts +20 -0
  239. package/src/types/data-overlay-polygon.ts +18 -0
  240. package/src/types/data-overlay.ts +16 -0
  241. package/src/types/demo-type.ts +4 -0
  242. package/src/types/gpt-question.ts +4 -0
  243. package/src/types/gpt-response-payload.ts +6 -0
  244. package/src/types/mcad-object-interaction.ts +15 -0
  245. package/src/types/segment-angle.ts +5 -0
  246. package/src/types/vector2.ts +4 -0
  247. package/src/types/vector3.ts +5 -0
  248. package/test/fakers/action-data.faker.ts +11 -0
  249. package/test/fakers/action.faker.ts +19 -0
  250. package/test/fakers/call-to-action.faker.ts +17 -0
  251. package/test/fakers/colour.faker.ts +17 -0
  252. package/test/fakers/data-overlay-angle.faker.ts +37 -0
  253. package/test/fakers/data-overlay-annotation.faker.ts +41 -0
  254. package/test/fakers/data-overlay-data.faker.ts +97 -0
  255. package/test/fakers/data-overlay-info.faker.ts +29 -0
  256. package/test/fakers/data-overlay-interaction.faker.ts +62 -0
  257. package/test/fakers/data-overlay-length.faker.ts +37 -0
  258. package/test/fakers/data-overlay-mcad.faker.ts +40 -0
  259. package/test/fakers/data-overlay-polygon.faker.ts +39 -0
  260. package/test/fakers/gpt-question.faker.ts +11 -0
  261. package/test/fakers/gpt-response.faker.ts +12 -0
  262. package/test/fakers/invert-transform.faker.ts +16 -0
  263. package/test/fakers/mcad-object-interaction.faker.ts +22 -0
  264. package/test/fakers/orientation.faker.ts +17 -0
  265. package/test/fakers/segment-angle.faker.ts +12 -0
  266. package/test/fakers/vector2.faker.ts +12 -0
  267. package/test/fakers/vector3.faker.ts +16 -0
  268. package/test/plugins/findByTestId.ts +25 -0
  269. package/test/plugins/vuetify.ts +16 -0
  270. package/test/setup.ts +16 -5
  271. package/tsconfig.json +4 -15
  272. package/vite.config.mts +20 -46
  273. package/vitest.config.mts +24 -43
  274. package/index.ts +0 -72
  275. package/src/assets/images/dark/3DICOM.png +0 -0
  276. package/src/assets/images/dark/3dicom-logo.svg +0 -1
  277. package/src/assets/images/light/3DICOM.png +0 -0
  278. package/src/assets/images/light/3dicom-logo.svg +0 -1
  279. package/src/assets/logo.png +0 -0
  280. package/src/assets/logo.svg +0 -6
  281. package/src/components/WebGL3DR.vue +0 -102
  282. package/src/components/__tests__/webgl3dr.spec.ts +0 -37
  283. package/src/components/icons/liver.vue +0 -21
  284. package/src/components/loading/__tests__/loading-spinner.spec.ts +0 -11
  285. package/src/components/modal/ViewerAnnotationModal.vue +0 -111
  286. package/src/components/modal/ViewerAnnotations.vue +0 -289
  287. package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +0 -79
  288. package/src/components/modal/composables/useEventListener.ts +0 -22
  289. package/src/components/selectors/__tests__/value-selector.spec.ts +0 -53
  290. package/src/components/sliders/DoubleSliderSelector.vue +0 -141
  291. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +0 -104
  292. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +0 -61
  293. package/src/dataLayer/__tests__/getIconForPreset.spec.ts +0 -40
  294. package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +0 -88
  295. package/src/dataLayer/__tests__/scanState.spec.ts +0 -93
  296. package/src/dataLayer/getIconForPreset.ts +0 -11
  297. package/src/dataLayer/patchDataOverlay.ts +0 -101
  298. package/src/dataLayer/useViewer3cr.ts +0 -7
  299. package/src/helpers/__tests__/layout-overlay-style.spec.ts +0 -290
  300. package/src/helpers/__tests__/utils.spec.ts +0 -70
  301. package/src/helpers/layoutOverlayStyle.ts +0 -96
  302. package/src/helpers/utils.ts +0 -16
  303. package/src/models/Callbacks.ts +0 -2
  304. package/src/models/LoadViewerOptions.ts +0 -31
  305. package/src/models/LoadViewerPayload.ts +0 -9
  306. package/src/models/__tests__/load-viewer-options.spec.ts +0 -22
  307. package/src/notifications/notification.ts +0 -50
  308. package/src/plugins/__tests__/index.spec.ts +0 -19
  309. package/src/plugins/index.ts +0 -17
  310. package/src/services/gpt/gpt.service.ts +0 -35
  311. package/static/3cr-types-browser/index.ts +0 -74
  312. package/static/3cr-types-browser/types/Action.ts +0 -6
  313. package/static/3cr-types-browser/types/AlphaKeys.ts +0 -5
  314. package/static/3cr-types-browser/types/AnchorPoint.ts +0 -12
  315. package/static/3cr-types-browser/types/CallToAction.ts +0 -5
  316. package/static/3cr-types-browser/types/ColourData.ts +0 -7
  317. package/static/3cr-types-browser/types/ColourPresetData.ts +0 -9
  318. package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +0 -6
  319. package/static/3cr-types-browser/types/CurrentScanState.ts +0 -22
  320. package/static/3cr-types-browser/types/DataOverlay.ts +0 -22
  321. package/static/3cr-types-browser/types/DataOverlayActions.ts +0 -14
  322. package/static/3cr-types-browser/types/DataOverlayData.ts +0 -8
  323. package/static/3cr-types-browser/types/DataOverlayEvent.ts +0 -8
  324. package/static/3cr-types-browser/types/DecryptionKey.ts +0 -4
  325. package/static/3cr-types-browser/types/DisplaySettings.ts +0 -10
  326. package/static/3cr-types-browser/types/EmptyPayload.ts +0 -3
  327. package/static/3cr-types-browser/types/EnumPayload.ts +0 -4
  328. package/static/3cr-types-browser/types/FileManagementActions.ts +0 -11
  329. package/static/3cr-types-browser/types/FlipValue.ts +0 -7
  330. package/static/3cr-types-browser/types/FrontEndInterfaces.ts +0 -14
  331. package/static/3cr-types-browser/types/GradientKeys.ts +0 -7
  332. package/static/3cr-types-browser/types/GreyscalePresetData.ts +0 -6
  333. package/static/3cr-types-browser/types/InitialDataOverlayState.ts +0 -6
  334. package/static/3cr-types-browser/types/InitialScanState.ts +0 -19
  335. package/static/3cr-types-browser/types/InteractionType.ts +0 -8
  336. package/static/3cr-types-browser/types/InteractivityActions.ts +0 -6
  337. package/static/3cr-types-browser/types/InteractivityState.ts +0 -4
  338. package/static/3cr-types-browser/types/InvertTransformData.ts +0 -6
  339. package/static/3cr-types-browser/types/LayoutActions.ts +0 -6
  340. package/static/3cr-types-browser/types/LayoutData.ts +0 -7
  341. package/static/3cr-types-browser/types/LoadDataSet.ts +0 -6
  342. package/static/3cr-types-browser/types/LoadSessionState.ts +0 -4
  343. package/static/3cr-types-browser/types/LocalLoadDataset.ts +0 -3
  344. package/static/3cr-types-browser/types/MovementData.ts +0 -7
  345. package/static/3cr-types-browser/types/NavigationCubeActions.ts +0 -8
  346. package/static/3cr-types-browser/types/NavigationCubeData.ts +0 -12
  347. package/static/3cr-types-browser/types/NavigationCubeTransform.ts +0 -9
  348. package/static/3cr-types-browser/types/NotificationPayload.ts +0 -7
  349. package/static/3cr-types-browser/types/NotificationsActions.ts +0 -6
  350. package/static/3cr-types-browser/types/Object.ts +0 -1
  351. package/static/3cr-types-browser/types/ObjectColour.ts +0 -7
  352. package/static/3cr-types-browser/types/ObjectIcon.ts +0 -5
  353. package/static/3cr-types-browser/types/ObjectInvert.ts +0 -7
  354. package/static/3cr-types-browser/types/ObjectSize.ts +0 -7
  355. package/static/3cr-types-browser/types/ObjectSize2D.ts +0 -7
  356. package/static/3cr-types-browser/types/ObjectVisible.ts +0 -5
  357. package/static/3cr-types-browser/types/PositionData.ts +0 -14
  358. package/static/3cr-types-browser/types/PresetsActions.ts +0 -4
  359. package/static/3cr-types-browser/types/RotationValue.ts +0 -7
  360. package/static/3cr-types-browser/types/ScanMovementActions.ts +0 -27
  361. package/static/3cr-types-browser/types/ScanMovementData.ts +0 -3
  362. package/static/3cr-types-browser/types/ScanOrientationActions.ts +0 -6
  363. package/static/3cr-types-browser/types/ScanStateActions.ts +0 -4
  364. package/static/3cr-types-browser/types/ScanView.ts +0 -6
  365. package/static/3cr-types-browser/types/SettingsData.ts +0 -12
  366. package/static/3cr-types-browser/types/SlicerData.ts +0 -9
  367. package/static/3cr-types-browser/types/SliderValue.ts +0 -4
  368. package/static/3cr-types-browser/types/SlidersActions.ts +0 -18
  369. package/static/3cr-types-browser/types/Vector2Data.ts +0 -5
  370. package/static/3cr-types-browser/types/Vector3Data.ts +0 -6
  371. package/static/3cr-types-browser/types/VectorMovementData.ts +0 -8
  372. package/static/3cr-types-browser/types/ViewInteractiveMode.ts +0 -5
  373. package/static/3cr-types-browser/types/ViewOrientation.ts +0 -8
  374. package/static/3cr-types-browser/types/ViewOrientations.ts +0 -10
  375. package/static/3cr-types-browser/types/ViewSelectionActions.ts +0 -9
  376. package/static/3cr-types-browser/types/ViewToggleData.ts +0 -7
  377. package/static/3cr-types-browser/types/VolumeOrientation.ts +0 -7
  378. package/test/helper.ts +0 -44
  379. /package/src/components/{sliders → shared}/VerticalSliderSelector.vue +0 -0
  380. /package/{config.ts → src/config.ts} +0 -0
  381. /package/src/{dataLayer → functions}/clamp.ts +0 -0
  382. /package/src/{dataLayer → models}/eventHandlers.ts +0 -0
  383. /package/{static/3cr-types-browser/types/ActionData.ts → src/types/action-data.ts} +0 -0
@@ -1,420 +1,240 @@
1
1
  <template>
2
- <DemoLicenceInfoModal v-model:modal="m_demo" :is-modal-open="value" />
3
- <DemoLicenceShareToMobileModal v-model:modal="m_demoLicenceShareToMobile" />
4
- <DemoLicenceSendToPartyModal v-model:modal="m_demoLicenceSendToParty" />
5
- <DemoLicenceEnableCloudStorageModal v-model:modal="m_demoLicenseEnableCloudStorage" />
6
- <DemoPatientInfoModal v-model:modal="m_demoPatient" :is-modal-open="value" />
7
- <DemoPatientShareToMobileModal v-model:modal="m_demoPatientShareToMobile" />
8
- <DemoPatientSendToPartyModal v-model:modal="m_demoPatientSendToParty" />
9
- <DemoPatientEnableCloudStorageModal v-model:modal="m_demoPatientEnableCloudStorage" />
10
- <CloseViewerModal v-model:modal="m_closeDialog" :options="opts" @close="value = false"/>
11
- <v-dialog
12
- data-vuetify
13
- :model-value="value"
14
- :no-click-animation="true"
15
- fullscreen
16
- persistent
17
- >
18
- <v-card
19
- id="invoice-table-modal"
20
- class="pa-0 ma-0 position-relative motif-background overflow-y-hidden rounded-0"
21
- height="100vh"
22
- >
23
- <v-app-bar density="compact">
24
- <v-menu :close-on-content-click="false">
25
- <template #activator="{ props, isActive }">
26
- <v-btn
27
- v-bind="props"
28
- class="mr-2"
29
- :color="isActive ? 'secondary' : 'primary'"
30
- prepend-icon="description"
31
- variant="flat"
32
- >
33
- File
34
- </v-btn>
35
- </template>
36
- <v-list>
37
- <v-list-item
38
- v-if="showOption('OnLoadNewDicomSeries')"
39
- @click="executeOption('OnLoadNewDicomSeries')"
40
- prepend-icon="upload"
41
- >
42
- <v-list-item-title>Load New DICOM Series</v-list-item-title>
43
- </v-list-item>
44
- <v-list-item
45
- v-if="showOption('OnDownloadDicomSeries')"
46
- @click="executeOption('OnDownloadDicomSeries')"
47
- prepend-icon="download"
48
- >
49
- <v-list-item-title>Download DICOM Series</v-list-item-title>
50
- </v-list-item>
51
- <v-list-item
52
- v-if="showOption('OnLoadSavedSession')"
53
- @click="executeOption('OnLoadSavedSession')"
54
- prepend-icon="sync"
55
- >
56
- <v-list-item-title> Load Saved Session </v-list-item-title>
57
- </v-list-item>
58
- <v-list-item
59
- v-if="showOption('OnSendTo3rdParty')"
60
- @click="executeOption('OnSendTo3rdParty')"
61
- prepend-icon="send"
62
- >
63
- <v-list-item-title>Send to 3rd Party</v-list-item-title>
64
- </v-list-item>
65
- <v-list-item
66
- v-if="showOption('OnShareToMobile')"
67
- @click="executeOption('OnShareToMobile')"
68
- prepend-icon="share"
69
- >
70
- <v-list-item-title>Share to Mobile / VR</v-list-item-title>
71
- </v-list-item>
72
-
73
- <v-list-item
74
- @click="
75
- executeOption('OnClosePopup');
76
- alterValue(false);
77
- "
78
- prepend-icon="close"
79
- >
80
- <v-list-item-title>Close Viewer</v-list-item-title>
81
- </v-list-item>
82
- </v-list>
83
- </v-menu>
84
- <v-menu
85
- v-model:model-value="introJsOpenMenu"
86
- :persistent="introJsDisableMenu"
87
- :no-click-animation="introJsDisableMenu"
88
- :close-on-content-click="false"
2
+ <v-app>
3
+ <v-app-bar density="compact">
4
+ <file-menu v-if="showFileMenu" @close="m_closeDialog = true" />
5
+ <settings-menu
6
+ v-model="introJsMenu"
7
+ v-if="showSettingsMenu"
8
+ :activator-props="{ disabled: introJsMenuDisabled }"
9
+ :persistent="introJsMenuDisabled"
10
+ :no-click-animation="introJsMenuDisabled"
11
+ />
12
+ <auto-annotate-btn v-if="showAiMenu" class="mr-2 transparent" />
13
+ <v-spacer />
14
+ <div class="font-weight-bold">
15
+ <span class="text-capitalize">{{ viewerTitle }}</span>
16
+ </div>
17
+ <v-spacer />
18
+ <v-btn
19
+ v-if="show2x2Layout"
20
+ class="ma-1 mr-0 pa-1"
21
+ data-testid="layout-2x2"
22
+ height="36"
23
+ style="min-width: 36px !important"
24
+ :color="isLayout2x2 ? 'primary' : 'default'"
25
+ @click="viewer3cr.layouts(LayoutActions.lo02)"
26
+ >
27
+ <v-icon>grid_view</v-icon>
28
+ <v-tooltip location="bottom" activator="parent"> Change Layout to 2:2 </v-tooltip>
29
+ </v-btn>
30
+ <v-btn
31
+ v-if="show1x3Layout"
32
+ class="ma-1 mr-1 pa-1"
33
+ data-testid="layout-1x3"
34
+ height="36"
35
+ style="min-width: 36px !important"
36
+ :color="isLayout1x3 ? 'primary' : 'default'"
37
+ @click="viewer3cr.layouts(LayoutActions.lo03)"
38
+ >
39
+ <v-icon style="rotate: -90deg">view_comfy</v-icon>
40
+ <v-tooltip location="bottom" activator="parent"> Change Layout to 1:3 </v-tooltip>
41
+ </v-btn>
42
+ <v-btn variant="flat" color="red" @click="m_closeDialog = true">Close Viewer</v-btn>
43
+ </v-app-bar>
44
+ <ViewerNavigationDrawer v-model:drawer="drawer" @update:expanded="expanded = $event" />
45
+ <v-main class="motif-background">
46
+ <LoadingSpinner v-if="scanLoading" text="Rendering your scan in 3D" />
47
+ <WebGL3DR
48
+ v-show="!scanLoading"
49
+ ref="webGl3dr"
50
+ @instance_loaded="load"
51
+ @dblclick="viewer3cr.viewSelection(ViewSelectionActions.vs05)"
52
+ @mouseenter="onMouseEnter"
53
+ @mouseleave="onMouseLeave"
54
+ >
55
+ <div
56
+ v-for="layout in scanState.Layout.PositionData"
57
+ :key="getCurrentView(layout)"
58
+ :style="generateDivStyleForLayout(layout, webGl3dr!.canvas)"
89
59
  >
90
- <template #activator="{ props, isActive }">
91
- <v-btn
92
- v-bind="props"
93
- :color="isActive ? 'secondary' : 'primary'"
94
- :disabled="introJsDisableMenu"
95
- prepend-icon="settings"
96
- variant="flat"
97
- >
98
- Settings
99
- </v-btn>
100
- </template>
101
- <v-card id="settings-card" width="350" class="pa-4">
102
- <ValueSelector
103
- v-model:value="scanState.Display.Brightness"
104
- label="Adjust Brightness"
105
- prepend="percent"
106
- />
107
- <ValueSelector
108
- v-model:value="scanState.Display.Contrast"
109
- label="Adjust Contrast"
110
- prepend="percent"
111
- />
112
- <ValueSelector
113
- v-model:value="scanState.Display.Opacity"
114
- label="Adjust Opacity"
115
- prepend="percent"
116
- />
117
- <v-divider class="my-4" />
118
- <ValueSelector
119
- v-model:value="scanState.InteractionSettings.PanSensivitity"
120
- :min="0"
121
- :max="100"
122
- label="Pan Sensitivity"
123
- prepend="percent"
124
- />
125
- <ValueSelector
126
- v-model:value="scanState.InteractionSettings.ZoomSensitivity"
127
- :min="0"
128
- :max="100"
129
- label="Zoom Sensitivity"
130
- prepend="percent"
131
- />
132
- <ValueSelector
133
- v-model:value="scanState.InteractionSettings.RotateSensitivity"
134
- :min="0"
135
- :max="100"
136
- label="Rotate Sensitivity"
137
- prepend="percent"
138
- />
139
- <ValueSelector
140
- v-model:value="
141
- scanState.InteractionSettings.CameraRotateSensitivity
142
- "
143
- :min="0"
144
- :max="100"
145
- label="Camera Rotate Sensitivity"
146
- prepend="percent"
147
- />
148
- <v-divider class="my-4" />
149
- <NavigationCubeAction />
150
- </v-card>
151
- </v-menu>
152
- <v-spacer />
153
- <div class="font-weight-bold">
154
- <span v-if="isDemo" class="text-capitalize">
155
- 3DICOM {{ demoType }} DEMO&nbsp;|
156
- </span>
157
- <span v-else class="text-capitalize">
158
- 3DICOM Patient Viewer&nbsp;|
159
- </span>
160
- Not for Diagnostic Use
60
+ <ViewerScanView class="w-100 h-100" :view="getCurrentView(layout)" />
161
61
  </div>
162
- <v-spacer />
163
- <v-btn
164
- class="ma-1 mr-0 pa-1"
165
- height="36"
166
- style="min-width: 36px !important"
167
- :color="isLayout2x2 ? 'secondary' : 'primary'"
168
- @click="viewer3cr.layouts(LayoutActions.lo02)"
169
- ><v-icon>grid_view</v-icon>
170
- <v-tooltip location="bottom" activator="parent">
171
- Change Layout to 2:2
172
- </v-tooltip>
173
- </v-btn>
174
- <v-btn
175
- class="ma-1 mr-1 pa-1"
176
- height="36"
177
- style="min-width: 36px !important"
178
- :color="isLayout1x3 ? 'secondary' : 'primary'"
179
- @click="viewer3cr.layouts(LayoutActions.lo03)"
180
- ><v-icon style="rotate: -90deg">view_comfy</v-icon>
181
- <v-tooltip location="bottom" activator="parent">
182
- Change Layout to 1:3
183
- </v-tooltip></v-btn
184
- >
185
- <v-btn variant="flat" color="red" @click="alterValue(false)">
186
- Close Viewer
187
- </v-btn>
188
- </v-app-bar>
189
- <ViewerNavigationDrawer v-model:drawer="drawer" :options="opts" @update:expanded="expanded = $event" />
190
- <v-main>
191
- <WebGL3DR
192
- v-show="value && instanceLoaded && !scanLoading"
193
- ref="webGl3dr"
194
- @instance_loaded="load"
195
- @dblclick="viewer3cr.viewSelection(ViewSelectionActions.vs05)"
196
- @mouseenter="onMouseEnter"
197
- @mouseleave="onMouseLeave"
198
- >
199
- <div
200
- v-for="(layout, index) in scanState.Layout.PositionData"
201
- :key="getCurrentView(layout)"
202
- :id="`view-${getCurrentView(layout)}`"
203
- :ref="(el) => (elements[index] = el)"
204
- class="bordered-event-window"
205
- :style="generateDivStyleForLayout(layout, webGl3dr!.canvas)"
206
- >
207
- <v-hover>
208
- <template #default="{ isHovering, props }">
209
- <v-row v-bind="props" class="flex-nowrap h-100" no-gutters>
210
- <v-col style="min-width: 0">
211
- <v-row class="flex-column h-100" no-gutters>
212
- <v-col class="flex-grow-0">
213
- <div class="d-flex pa-2 pl-3 align-center">
214
- <span style="color: white">{{ getViewName(getCurrentView(layout)) }}</span>
215
- <v-tooltip v-if="isHorizontalFlip(getCurrentView(layout))" location="bottom">
216
- <template #activator="{ props }">
217
- <v-icon
218
- v-bind="props"
219
- icon="swap_horiz"
220
- color="blue-darken-1"
221
- class="ml-2"
222
- />
223
- </template>
224
- <span>This view has been flipped horizontally</span>
225
- </v-tooltip>
226
- <v-tooltip v-if="isVerticalFlip(getCurrentView(layout))" location="bottom">
227
- <template #activator="{ props }">
228
- <v-icon
229
- v-bind="props"
230
- icon="swap_vert"
231
- color="blue-darken-1"
232
- class="ml-2"
233
- />
234
- </template>
235
- <span>This view has been flipped vertically</span>
236
- </v-tooltip>
237
- </div>
238
- </v-col>
239
- <v-spacer />
240
- <v-col class="flex-grow-0">
241
- <Transition>
242
- <div v-show="isHovering || modals[getCurrentView(layout)]">
243
- <ViewerActionRail
244
- :view="getCurrentView(layout)"
245
- :element="elements[index]"
246
- @modal="onActionModal"
247
- />
248
- </div>
249
- </Transition>
250
- </v-col>
251
- </v-row>
252
- </v-col>
253
- <v-col class="flex-grow-0">
254
- <VerticalSliderSelector
255
- class="h-100 my-0 pa-2 pr-3"
256
- v-if="getCurrentView(layout) === ScanView.Transverse"
257
- v-model:value="scanState.Orientations.Transverse.Slice"
258
- v-bind="tMinMax"
259
- />
260
- <VerticalSliderSelector
261
- class="h-100 my-0 pa-2 pr-3"
262
- v-if="getCurrentView(layout) === ScanView.Coronal"
263
- v-model:value="scanState.Orientations.Coronal.Slice"
264
- v-bind="cMinMax"
265
- />
266
- <VerticalSliderSelector
267
- class="h-100 my-0 pa-2 pr-3"
268
- v-if="getCurrentView(layout) === ScanView.Sagittal"
269
- v-model:value="scanState.Orientations.Sagittal.Slice"
270
- v-bind="sMinMax"
271
- />
272
- </v-col>
273
- </v-row>
274
- </template>
275
- </v-hover>
276
- </div>
277
- </WebGL3DR>
278
- <ViewerAnnotationModal />
279
- </v-main>
280
- <LoadingSpinner v-if="!instanceLoaded" />
281
- <LoadingSpinner v-if="scanLoading" text="Rendering your scan in 3D" />
282
- </v-card>
283
- </v-dialog>
62
+ </WebGL3DR>
63
+ <data-overlay-modal />
64
+ </v-main>
65
+ <CloseViewerModal v-model:modal="m_closeDialog" />
66
+ </v-app>
284
67
  </template>
285
68
 
286
- <!-- /* c8 ignore stop */ -->
287
69
  <script setup lang="ts">
288
- import { generateDivStyleForLayout } from "@/helpers/layoutOverlayStyle";
70
+ import { generateDivStyleForLayout } from '@/functions/layoutOverlayStyle';
289
71
  import {
290
- DataOverlayActions,
72
+ DataOverlaysActions,
291
73
  FileManagementActions,
292
74
  FrontEndInterfaces,
75
+ GraphicType,
293
76
  LayoutActions,
77
+ McadActions,
294
78
  NotificationsActions,
295
- ScanMovementActions,
296
79
  ScanStateActions,
297
- ScanView,
298
- SlidersActions,
299
- ViewSelectionActions,
300
- } from "@3cr/types-ts";
301
- import {ComponentInstance, computed, nextTick, onBeforeUnmount, onMounted, ref, unref, watch, WatchSource} from "vue";
302
- import {
303
- checkIsDemo,
304
- demoOptions,
305
- demoType,
306
- isDemo,
307
- m_demo,
308
- m_demoLicenceSendToParty,
309
- m_demoLicenceShareToMobile,
310
- m_demoLicenseEnableCloudStorage,
311
- m_demoPatient,
312
- m_demoPatientEnableCloudStorage,
313
- m_demoPatientSendToParty,
314
- m_demoPatientShareToMobile,
315
- } from "@/components/demo/options";
316
- import { handleNotification } from "@/notifications/notification";
317
- import { LoadViewerPayload } from "@/models/LoadViewerPayload";
318
- import {
319
- defaultLoadViewerOptions,
320
- LoadViewerOptions,
321
- } from "@/models/LoadViewerOptions";
80
+ ViewSelectionActions
81
+ } from '@3cr/types-ts';
82
+ import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
83
+ import { checkIsDemo, demoOptions, demoType, isDemo } from '@/components/demo/options';
84
+ import { handleNotification } from '@/functions/notification';
85
+ import { defaultLoadViewerPayload, LoadViewerPayload } from '@/models/loadViewerPayload';
86
+ import { defaultLoadViewerOptions, LoadViewerOptions } from '@/models/loadViewerOptions';
322
87
  import {
323
- cMinMax,
324
88
  getCurrentView,
325
- getViewName,
326
- initialScanState,
327
- isFullscreen,
328
- isHorizontalFlip,
329
89
  isLayout1x3,
330
90
  isLayout2x2,
331
- isVerticalFlip,
332
- previousLayout,
333
91
  scanState,
334
- setActiveAnnotation,
92
+ setDataOverlayEvent,
335
93
  setDataOverlayState,
336
94
  setInitialDataOverlayState,
95
+ setInitialMcadState,
337
96
  setInitialScanStateFromPayload,
97
+ setMcadEvent,
98
+ setMcadState,
338
99
  setScanStateFromPayload,
339
- sMinMax,
340
- thresholdSlider,
341
- tMinMax,
342
- transactionStarted,
343
- windowSlider,
344
- } from "@/dataLayer/scanState";
345
- import introJs from "intro.js";
346
- import type { IntroJs } from "intro.js/src/intro";
347
- import "intro.js/introjs.css";
348
- import WebGL3DR from "@/components/WebGL3DR.vue";
349
- import { useViewer3cr } from "@/dataLayer/useViewer3cr";
350
- import { useNavigationCubeObserver } from "@/components/modal/composables/useNavigationCubeObserver";
351
-
352
- export interface Props {
100
+ transactionStarted
101
+ } from '@/models/scanState';
102
+ import { useViewer3cr } from '@/composables/useViewer3cr';
103
+ import { useNavigationCubeObserver } from '@/composables/useNavigationCubeObserver';
104
+ import { mockDemoViewerStlDecimator, useViewerOptions } from '@/composables/useViewerOptions';
105
+ import { useScanMovement } from '@/composables/useScanMovement';
106
+ import { useScanSliders } from '@/composables/useScanSliders';
107
+ import { useIntroJs } from '@/composables/useIntroJs';
108
+ import WebGL3DR from '@/components/modal/WebGL3DR.vue';
109
+ import { DemoType } from '@/types/demo-type';
110
+
111
+ interface Props {
353
112
  payload?: LoadViewerPayload;
354
113
  options?: LoadViewerOptions;
355
114
  }
356
115
 
357
- const emit = defineEmits<{
358
- instanceLoaded: [void];
359
- }>();
360
-
361
116
  const props = withDefaults(defineProps<Props>(), {
362
- payload: () => ({
363
- Url: "https://webgl-3dr.singular.health/test_scans/8bdddee1-e581-485d-827d-6aa12eef2fc8/Head+Axial+Axial.3vxl",
364
- DecryptionKey: {
365
- Iv: "x856FgjpYDsRhIa3BFj5cg==",
366
- Key: "OWjSMiL/ewUV1V6fGybhKcTyiysTPsIMp2DjdVoOUGI=",
367
- },
368
- }),
369
- options: () => defaultLoadViewerOptions,
117
+ payload: () => defaultLoadViewerPayload,
118
+ options: () => defaultLoadViewerOptions
370
119
  });
371
120
 
121
+ useScanMovement();
122
+ useScanSliders();
372
123
  const viewer3cr = useViewer3cr();
124
+ const opts = useViewerOptions();
125
+ const { intro } = useIntroJs();
373
126
  const webGl3dr = ref<typeof WebGL3DR>();
374
127
  const observer = ref<ResizeObserver>();
375
- const value = ref<boolean>(false);
376
128
  const drawer = ref<boolean>(false);
377
129
  const expanded = ref<number | undefined>();
378
130
  const scanLoading = ref<boolean>(true);
379
- const instanceLoaded = ref<boolean>(true);
380
- const elements = ref<ComponentInstance<any>[]>([]);
381
131
  const m_closeDialog = ref<boolean>(false);
382
- const introJsOpenMenu = ref<boolean>(false);
383
- const introJsDisableMenu = ref<boolean>(false);
384
- const modals = ref<Record<ScanView, boolean>>({
385
- [ScanView.Volume]: false,
386
- [ScanView.Sagittal]: false,
387
- [ScanView.Coronal]: false,
388
- [ScanView.Transverse]: false,
132
+ const introJsMenu = ref<boolean>(false);
133
+ const introJsMenuDisabled = ref<boolean>(false);
134
+
135
+ const demo = computed<string>(() => {
136
+ switch (demoType.value) {
137
+ case DemoType.Licence:
138
+ return 'Licence';
139
+ default:
140
+ return 'Patient';
141
+ }
389
142
  });
390
143
 
391
- const tier = computed(() => {
392
- const value = demoType.value;
393
- return value.charAt(0).toUpperCase() + value.substring(1);
394
- });
144
+ const showFileMenu = computed(() => opts.value.ShowFileMenu !== false);
145
+
146
+ const showSettingsMenu = computed(() => opts.value.ShowSettingsMenu !== false);
147
+
148
+ const showAiMenu = computed(() => opts.value.ShowAiMenu !== false);
149
+
150
+ const show2x2Layout = computed(() => opts.value.Show2x2Layout !== false);
395
151
 
396
- const opts = computed(() => {
397
- return unref(isDemo) ? demoOptions.value : props.options;
152
+ const show1x3Layout = computed(() => opts.value.Show1x3Layout !== false);
153
+
154
+ const viewerTitle = computed(() => {
155
+ return (
156
+ opts.value.ViewerTitle ??
157
+ `3DICOM ${isDemo.value ? `${demo.value} DEMO` : 'Patient Viewer'} | Not for Diagnostic Use`
158
+ );
398
159
  });
399
160
 
161
+ watch(
162
+ props,
163
+ (props) => {
164
+ checkIsDemo(props.payload);
165
+ opts.value = isDemo.value ? demoOptions.value : props.options;
166
+ },
167
+ { immediate: true }
168
+ );
169
+
170
+ /* istanbul ignore next -- @preserve */
171
+ watch(
172
+ () => intro.value.getCurrentStep(),
173
+ (step) => {
174
+ const lock = step === 4;
175
+ introJsMenu.value = lock;
176
+ introJsMenuDisabled.value = lock;
177
+ setTimeout(() => {
178
+ if (intro.value.isActive()) {
179
+ intro.value.refresh(true);
180
+ }
181
+ }, 400);
182
+ }
183
+ );
184
+
185
+ /* istanbul ignore next -- @preserve */
400
186
  onMounted(() => {
401
187
  viewer3cr.addInterfaceHandler(FrontEndInterfaces.notifications, async (message: string, action: string) => {
402
188
  await handleNotification(action as NotificationsActions, message);
403
189
  });
404
190
 
405
191
  viewer3cr.addActionHandler(FileManagementActions.fm02, async (message: string) => {
192
+ // Set up the scan state
406
193
  setInitialScanStateFromPayload(message);
407
- await nextTick();
408
194
  transactionStarted.value = false;
409
195
  scanLoading.value = false;
410
196
  drawer.value = true;
411
- if (unref(isDemo)) {
412
- await viewer3cr.loadDataOverlay("https://webgl-3dr.singular.health/test_scans/sample_annotations.json");
197
+ await nextTick();
198
+ if (opts.value.OnScanLoaded) {
199
+ await opts.value.OnScanLoaded(scanState.value);
413
200
  }
201
+
202
+ // Set up the navigation cube
414
203
  observer.value = useNavigationCubeObserver(webGl3dr.value!.canvas);
415
- await startIntro();
204
+ switch (demoType.value) {
205
+ case DemoType.Licence:
206
+ await viewer3cr.setNavCubeGraphics(GraphicType.LETTER);
207
+ break;
208
+ default:
209
+ await viewer3cr.setNavCubeGraphics(GraphicType.BODY_SYMBOL);
210
+ break;
211
+ }
212
+
213
+ // Load the demo data
214
+ await loadDemoData();
215
+
216
+ // Load the tutorial
217
+ if (opts.value.ShowTutorial !== false) {
218
+ await intro.value.start();
219
+ if (!intro.value.isActive() && opts.value.OnInteractionReady) {
220
+ await opts.value.OnInteractionReady();
221
+ }
222
+ }
416
223
  });
417
224
 
225
+ viewer3cr.addActionHandler(
226
+ FileManagementActions.fm09,
227
+ async (_1: string, returnChannel: string, returnTo: string) => {
228
+ if (opts.value.OnResolveUrls) {
229
+ const keys = JSON.parse(returnChannel) as string[];
230
+ const urls = await opts.value.OnResolveUrls(keys);
231
+ await viewer3cr.loadMcadObjects(undefined, urls, returnTo);
232
+ } else {
233
+ throw new Error('OnResolveUrls has not been defined in LoadViewerOptions');
234
+ }
235
+ }
236
+ );
237
+
418
238
  viewer3cr.addActionHandler(ScanStateActions.ss01, async (message: string) => {
419
239
  await setScanStateFromPayload(message);
420
240
  });
@@ -423,16 +243,36 @@ onMounted(() => {
423
243
  setDataOverlayState(message);
424
244
  });
425
245
 
426
- viewer3cr.addActionHandler(DataOverlayActions.do01, (message: string) => {
246
+ viewer3cr.addActionHandler(ScanStateActions.ss03, (message: string) => {
247
+ setMcadState(message);
248
+ });
249
+
250
+ viewer3cr.addActionHandler(DataOverlaysActions.do01, (message: string) => {
427
251
  setInitialDataOverlayState(message);
428
252
  });
429
253
 
430
- viewer3cr.addActionHandler(DataOverlayActions.do02, (message: string) => {
431
- setActiveAnnotation(message);
254
+ viewer3cr.addActionHandler(DataOverlaysActions.do02, (message: string) => {
255
+ setDataOverlayEvent(message);
256
+ });
257
+
258
+ viewer3cr.addActionHandler(DataOverlaysActions.do07, (message: string) => {
259
+ setDataOverlayEvent(message);
260
+ });
261
+
262
+ viewer3cr.addActionHandler('mu_17', (message: string) => {
263
+ setDataOverlayEvent(message);
432
264
  });
433
265
 
434
- viewer3cr.addActionHandler(DataOverlayActions.do07, (message: string) => {
435
- setActiveAnnotation(message);
266
+ viewer3cr.addActionHandler('mu_18', (message: string) => {
267
+ setDataOverlayEvent(message);
268
+ });
269
+
270
+ viewer3cr.addActionHandler(McadActions.mc01, (message: string) => {
271
+ setInitialMcadState(message);
272
+ });
273
+
274
+ viewer3cr.addActionHandler(McadActions.mc02, (message: string) => {
275
+ setMcadEvent(message);
436
276
  });
437
277
  });
438
278
 
@@ -442,106 +282,30 @@ onBeforeUnmount(() => {
442
282
  }
443
283
  });
444
284
 
445
- function showOption(key: keyof LoadViewerOptions): boolean {
446
- return opts.value[key] !== undefined;
447
- }
448
-
449
- async function executeOption(key: keyof LoadViewerOptions) {
450
- const functionToExecute = opts.value[key];
451
- if (functionToExecute !== undefined) {
452
- await functionToExecute();
453
- }
454
- }
455
-
456
- function onActionModal(value: boolean, view: ScanView): void {
457
- modals.value[view] = value;
458
- }
459
-
460
- type WatchSlidersType = { [key in SlidersActions]: WatchSource<number> };
461
- const watchSliders: WatchSlidersType = {
462
- [SlidersActions.sl01]: () => scanState.value.Display.Brightness,
463
- [SlidersActions.sl02]: () => scanState.value.Display.Contrast,
464
- [SlidersActions.sl03]: () => scanState.value.Display.Opacity,
465
- [SlidersActions.sl04]: () => scanState.value.Display.WindowLower,
466
- [SlidersActions.sl05]: () => scanState.value.Display.WindowUpper,
467
- [SlidersActions.sl06]: () => scanState.value.Display.ThresholdLower,
468
- [SlidersActions.sl07]: () => scanState.value.Display.ThresholdUpper,
469
- [SlidersActions.sl08]: () => scanState.value.Slice.TransverseLower,
470
- [SlidersActions.sl09]: () => scanState.value.Orientations.Transverse.Slice,
471
- [SlidersActions.sl10]: () => scanState.value.Slice.TransverseUpper,
472
- [SlidersActions.sl11]: () => scanState.value.Slice.SagittalLower,
473
- [SlidersActions.sl12]: () => scanState.value.Orientations.Sagittal.Slice,
474
- [SlidersActions.sl13]: () => scanState.value.Slice.SagittalUpper,
475
- [SlidersActions.sl14]: () => scanState.value.Slice.CoronalLower,
476
- [SlidersActions.sl15]: () => scanState.value.Orientations.Coronal.Slice,
477
- [SlidersActions.sl16]: () => scanState.value.Slice.CoronalUpper,
478
- };
479
-
480
- for (const slider of Object.keys(watchSliders)) {
481
- watch(
482
- watchSliders[slider as keyof WatchSlidersType],
483
- async (value: number) => {
484
- await viewer3cr.sliderHandler(slider as SlidersActions, value);
485
- }
486
- );
487
- }
488
-
489
- type WatchScanMovementType = {
490
- [key in ScanMovementActions]: WatchSource<number>;
491
- };
492
-
493
- //@ts-ignore
494
- const watchScanMovement: WatchScanMovementType = {
495
- [ScanMovementActions.sm05]: () =>
496
- scanState.value.InteractionSettings.PanSensivitity,
497
- [ScanMovementActions.sm08]: () =>
498
- scanState.value.InteractionSettings.ZoomSensitivity,
499
- [ScanMovementActions.sm10]: () =>
500
- scanState.value.InteractionSettings.RotateSensitivity,
501
- [ScanMovementActions.sm12]: () =>
502
- scanState.value.InteractionSettings.CameraRotateSensitivity,
503
- };
504
-
505
- for (const scanMovement of Object.keys(watchScanMovement)) {
506
- watch(
507
- watchScanMovement[scanMovement as keyof typeof watchScanMovement],
508
- async (value: number) => {
509
- await viewer3cr.scanMovementHandler(
510
- scanMovement as ScanMovementActions,
511
- value
512
- );
513
- }
514
- );
515
- }
516
-
517
- function alterValue(val: boolean) {
518
- if (!val) {
519
- m_closeDialog.value = true;
520
- return;
521
- } else {
522
- checkIsDemo(props.payload);
523
- }
524
- value.value = val;
525
- }
526
-
285
+ /* istanbul ignore next -- @preserve */
527
286
  async function loadSession(url: string): Promise<void> {
528
- if (url.includes('3crds')) {
529
- await viewer3cr.loadDataOverlay(url);
287
+ if (url.includes('3crs')) {
288
+ await viewer3cr.loadScanSession({ Url: url });
289
+ } else if (url.includes('3crds')) {
290
+ await viewer3cr.setDataOverlaySession({ Url: url });
530
291
  } else if (url.includes('3crms')) {
531
- await viewer3cr.loadMcad(url);
532
- } else if (url.includes('3crs')) {
533
- await viewer3cr.loadScanSession(url);
292
+ await viewer3cr.loadMcadObjects({ Url: url });
534
293
  } else {
535
294
  throw new Error('Invalid URL type');
536
295
  }
537
296
  }
538
297
 
539
298
  async function load(): Promise<void> {
540
- instanceLoaded.value = true;
541
299
  scanLoading.value = true;
542
300
  await viewer3cr.loadScan(props.payload);
543
301
  }
544
302
 
303
+ async function close(): Promise<void> {
304
+ if (opts.value.OnExitViewer) {
305
+ await opts.value.OnExitViewer();
306
+ }
307
+ }
308
+
545
309
  async function onMouseEnter(): Promise<void> {
546
310
  await viewer3cr.hoverOverCanvas(true);
547
311
  }
@@ -550,192 +314,23 @@ async function onMouseLeave(): Promise<void> {
550
314
  await viewer3cr.hoverOverCanvas(false);
551
315
  }
552
316
 
553
- async function startIntro(): Promise<void> {
554
- let intro: IntroJs | null = null;
555
-
556
- const sleep = (delay: number) =>
557
- new Promise((resolve) => setTimeout(resolve, delay));
558
-
559
- const refreshHandle = watch(scanState, () => {
560
- intro?.refresh(true);
561
- });
562
-
563
- const expandHandle = watch(expanded, async () => {
564
- // For some reason introjs refresh by itself doesn't work here
565
- if (intro && intro.currentStep() === 1) {
566
- await sleep(400);
567
- const skinToBone = document.getElementById('skin-to-bone');
568
- const floatingElement = document.querySelector('.introjsFloatingElement') as HTMLElement | null;
569
- intro._introItems[1].element = skinToBone ?? floatingElement;
570
- intro.refresh(true);
571
- }
572
- });
573
-
574
- const blurActiveElement = () => {
575
- (document.activeElement as HTMLElement).blur();
576
- };
577
-
578
- intro = await introJs()
579
- .setOptions({
580
- exitOnOverlayClick: false,
581
- keyboardNavigation: false,
582
- dontShowAgain: true,
583
- steps: [
584
- {
585
- title: "Take the Viewer Tour",
586
- intro:
587
- `Welcome to the ${tier.value} Online DICOM Viewer.` +
588
- "<br/><br/>Take the short interative tour to better visualize your medical images in 2D and 3D.",
589
- position: "floating",
590
- },
591
- {
592
- title: "Density Slider",
593
- intro:
594
- "Move the <em>lower density slider</em> to the right to hide softer tissue like skin and view internal organs." +
595
- "<br/><br/>For more precise control, change the numbers in the boxes above or use the <em>Fine Adjustment</em> slider below.",
596
- element: "#skin-to-bone",
597
- position: "right",
598
- },
599
- {
600
- title: "3D Viewing Controls",
601
- intro:
602
- "With your cursor placed in the 3D view, <b>scroll to zoom</b> & <b>click and drag to rotate</b>." +
603
- "<br/><br/>You can also view the 3D view in full screen, reset the view, and cut into the 3D model from each side using the <em>3D slicing tool</em>.",
604
- element: `#view-${ScanView.Volume}`,
605
- },
606
- {
607
- title: "2D Viewing Controls",
608
- intro:
609
- "With your mouse in the 2D views, <b>scroll to navigate through the 2D images</b>, and use <b>Shift + Arrows to Pan and Zoom</b>." +
610
- "<br/><br/>Use the icons to <em>Fullscreen</em>, <em>Reset</em>, <em>Rotate</em>, and <em>Flip</em> each individual 2D view.",
611
- element: `#view-${ScanView.Sagittal}`,
612
- },
613
- {
614
- title: "Global settings",
615
- intro:
616
- "Accurately change the <b>brightness</b>, <b>contrast</b>, and <b>opacity</b> to enhance the appearance of your medical images." +
617
- "<br/><br/>Adjust the sensitivity of mouse and keyboard inputs to suit your preferences.",
618
- element: "#settings-card",
619
- position: "right",
620
- },
621
- {
622
- title: "Stay Tuned for Updates",
623
- intro:
624
- '<img src="https://c.tenor.com/S4Sz_yvlLn4AAAAC/cats-cat.gif" alt="cat" width="260" height="260">' +
625
- "<br/>Us working hard on new features",
626
- position: "floating",
627
- },
628
- ],
629
- })
630
- .onbeforechange(function (
631
- el: HTMLElement,
632
- step: number,
633
- direction: "forward" | "backward"
634
- ) {
635
- // Focus on volume view if moving to step 2
636
- modals.value[ScanView.Volume] = step === 2;
637
-
638
- // Focus on sagittal view if moving to step 3
639
- modals.value[ScanView.Sagittal] = step === 3;
640
-
641
- // If the user has enabled full-screen, turn it off for steps 2 and 3
642
- if (isFullscreen.value && (step === 2 || step === 3)) {
643
- viewer3cr
644
- .layouts(previousLayout.value)
645
- .then(() => nextTick())
646
- .then(() => this.refresh(true))
647
- .then(() => direction === "forward" ? this.nextStep() : this.previousStep());
648
- return false;
649
- }
650
-
651
- // Wait for menu animation before focusing if moving to step 4
652
- if (!introJsOpenMenu.value && step === 4) {
653
- introJsOpenMenu.value = true;
654
- introJsDisableMenu.value = true;
655
- sleep(400)
656
- .then(() => this.refresh(true))
657
- .then(() => direction === "forward" ? this.nextStep() : this.previousStep());
658
- return false;
659
- } else if (introJsOpenMenu.value && step !== 4) {
660
- introJsOpenMenu.value = false;
661
- introJsDisableMenu.value = false;
662
- }
663
-
664
- // Only show Don't Show Again on last step
665
- const introjsDontShowAgain = document.querySelector(".introjs-dontShowAgain") as HTMLElement | null;
666
- if (introjsDontShowAgain) {
667
- const lastStep = this._options.steps.length - 1;
668
- introjsDontShowAgain.style.display = step !== lastStep ? "none" : "block";
669
- }
670
-
671
- return true;
672
- })
673
- .onchange(() => {
674
- setTimeout(() => blurActiveElement(), 500);
675
- })
676
- .onexit(() => {
677
- introJsOpenMenu.value = false;
678
- introJsDisableMenu.value = false;
679
- setTimeout(() => blurActiveElement(), 500);
680
- refreshHandle();
681
- expandHandle();
682
- })
683
- .start();
317
+ /* istanbul ignore next -- @preserve */
318
+ async function loadDemoData(): Promise<void> {
319
+ if (isDemo.value) {
320
+ const BASE_STATE_URL = 'https://webgl-3dr.singular.health/demo';
321
+ mockDemoViewerStlDecimator();
322
+ await viewer3cr.addDataOverlaySession({ Url: `${BASE_STATE_URL}/state/data_overlay.3crds` });
323
+ await viewer3cr.addDataOverlaySession({ Url: `${BASE_STATE_URL}/state/markups_2.3crds` });
324
+ // await viewer3cr.loadMcadObjects({ Url: `${BASE_STATE_URL}/state/mcad.3crms` });
325
+ }
684
326
  }
685
327
 
686
- defineExpose({
687
- alterValue,
688
- loadSession,
689
- load,
690
- startIntro,
691
- thresholdSlider,
692
- windowSlider,
693
- m_closeDialog,
694
- scanState,
695
- initialScanState,
696
- value,
697
- transactionStarted,
698
- });
328
+ defineExpose({ loadSession, load, close, m_closeDialog });
699
329
  </script>
700
330
 
701
331
  <style>
702
- #view-0 {
703
- cursor: grab;
704
- }
705
-
706
- .bordered-event-window {
707
- position: absolute;
708
- border: 1px rgba(128, 128, 128, 0.56) solid;
709
- transition: border 0.5s;
710
- }
711
-
712
- .bordered-event-window:hover {
713
- border: 1px dashed rgb(0, 152, 253);
714
- }
715
-
716
332
  .motif-background {
717
333
  background: url("data:image/svg+xml,%3Csvg width='992' height='560' viewBox='15 7 992 560' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='15' y='1' width='992' height='567' fill='url(%23paint0_linear_504_579)'/%3E%3Cg filter='url(%23filter0_i_504_579)'%3E%3Cpath d='M567.029 291.5C574.21 421.339 748.15 561 825.931 561H12L12.0001 0C153.713 0 580.363 0 580.363 0C684.258 142 560.918 181 567.029 291.5Z' fill='%231B2E43' fill-opacity='0.9'/%3E%3C/g%3E%3Cg filter='url(%23filter1_i_504_579)'%3E%3Cpath d='M435.911 260.5C462.579 433.5 652.034 561 729.815 561H12L12.0001 0C233.678 0 409.954 92.1126 435.911 260.5Z' fill='%231B2E43' fill-opacity='0.9'/%3E%3C/g%3E%3Cg filter='url(%23filter2_i_504_579)'%3E%3Ccircle cx='46' cy='521' r='93' fill='%231B2E43' fill-opacity='0.3'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='filter0_i_504_579' x='12' y='0' width='858.931' height='606' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='BackgroundImageFix' result='shape'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='45' dy='45'/%3E%3CfeGaussianBlur stdDeviation='50'/%3E%3CfeComposite in2='hardAlpha' operator='arithmetic' k2='-1' k3='1'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.653125 0 0 0 0 0.761642 0 0 0 0 0.916667 0 0 0 0.15 0'/%3E%3CfeBlend mode='normal' in2='shape' result='effect1_innerShadow_504_579'/%3E%3C/filter%3E%3Cfilter id='filter1_i_504_579' x='12' y='0' width='762.815' height='606' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='BackgroundImageFix' result='shape'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='45' dy='45'/%3E%3CfeGaussianBlur stdDeviation='50'/%3E%3CfeComposite in2='hardAlpha' operator='arithmetic' k2='-1' k3='1'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.653125 0 0 0 0 0.761642 0 0 0 0 0.916667 0 0 0 0.25 0'/%3E%3CfeBlend mode='normal' in2='shape' result='effect1_innerShadow_504_579'/%3E%3C/filter%3E%3Cfilter id='filter2_i_504_579' x='-47' y='428' width='231' height='231' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='BackgroundImageFix'/%3E%3CfeBlend mode='normal' in='SourceGraphic' in2='BackgroundImageFix' result='shape'/%3E%3CfeColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='hardAlpha'/%3E%3CfeOffset dx='45' dy='45'/%3E%3CfeGaussianBlur stdDeviation='50'/%3E%3CfeComposite in2='hardAlpha' operator='arithmetic' k2='-1' k3='1'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.653125 0 0 0 0 0.761642 0 0 0 0 0.916667 0 0 0 0.15 0'/%3E%3CfeBlend mode='normal' in2='shape' result='effect1_innerShadow_504_579'/%3E%3C/filter%3E%3ClinearGradient id='paint0_linear_504_579' x1='1251.04' y1='696.83' x2='21.486' y2='-10.2783' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%230E141D'/%3E%3Cstop offset='1' stop-color='%2323405E'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E%0A") !important;
718
334
  background-size: cover !important;
719
335
  }
720
-
721
- .introjs-tooltipReferenceLayer {
722
- z-index: 2401;
723
- }
724
-
725
- .introjs-overlay, .introjs-helperLayer {
726
- pointer-events: none;
727
- z-index: 2401;
728
- }
729
-
730
- .introjs-dontShowAgain {
731
- display: none;
732
- }
733
-
734
- .v-enter-active, .v-leave-active {
735
- transition: opacity 0.5s ease;
736
- }
737
-
738
- .v-enter-from, .v-leave-to {
739
- opacity: 0;
740
- }
741
336
  </style>