@3cr/viewer-browser 0.0.53 → 0.0.57

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 (72) hide show
  1. package/__tests__/index.spec.ts +31 -0
  2. package/components.d.ts +3 -2
  3. package/coverage/3cr-viewer-browser/index.html +116 -0
  4. package/coverage/3cr-viewer-browser/index.ts.html +211 -0
  5. package/coverage/3cr-viewer-browser/src/App.vue.html +313 -0
  6. package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +442 -0
  7. package/coverage/3cr-viewer-browser/src/components/icons/index.html +116 -0
  8. package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +148 -0
  9. package/coverage/3cr-viewer-browser/src/components/index.html +116 -0
  10. package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +556 -0
  11. package/coverage/3cr-viewer-browser/src/components/loading/index.html +116 -0
  12. package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +4126 -0
  13. package/coverage/3cr-viewer-browser/src/components/modal/index.html +116 -0
  14. package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +331 -0
  15. package/coverage/3cr-viewer-browser/src/components/selectors/index.html +116 -0
  16. package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +445 -0
  17. package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +349 -0
  18. package/coverage/3cr-viewer-browser/src/components/sliders/index.html +131 -0
  19. package/coverage/3cr-viewer-browser/src/helpers/index.html +146 -0
  20. package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +406 -0
  21. package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +412 -0
  22. package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +133 -0
  23. package/coverage/3cr-viewer-browser/src/index.html +131 -0
  24. package/coverage/3cr-viewer-browser/src/main.ts.html +124 -0
  25. package/coverage/3cr-viewer-browser/src/plugins/index.html +131 -0
  26. package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +130 -0
  27. package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +220 -0
  28. package/coverage/base.css +224 -0
  29. package/coverage/block-navigation.js +87 -0
  30. package/coverage/favicon.png +0 -0
  31. package/coverage/index.html +251 -0
  32. package/coverage/prettify.css +1 -0
  33. package/coverage/prettify.js +2 -0
  34. package/coverage/sort-arrow-sprite.png +0 -0
  35. package/coverage/sorter.js +196 -0
  36. package/dist/Viewer3CR.js +17 -11
  37. package/dist/Viewer3CR.mjs +11911 -11157
  38. package/dist/Viewer3CR.umd.js +17 -11
  39. package/index.html +4 -1
  40. package/index.ts +46 -11
  41. package/package.json +9 -3
  42. package/src/App.vue +34 -45
  43. package/src/__tests__/app.spec.ts +27 -0
  44. package/src/components/WebGL3DR.vue +49 -37
  45. package/src/components/__tests__/webgl3dr.spec.ts +56 -0
  46. package/src/components/icons/liver.vue +21 -0
  47. package/src/components/loading/LoadingSpinner.vue +34 -12
  48. package/src/components/loading/__tests__/loading-spinner.spec.ts +11 -0
  49. package/src/components/modal/DemoModal.vue +44 -0
  50. package/src/components/modal/MftpWebGL3DRModal.vue +763 -410
  51. package/src/components/modal/__tests__/mftp-webgl-3dr-modal.spec.ts +690 -0
  52. package/src/components/selectors/__tests__/value-selector.spec.ts +35 -0
  53. package/src/components/sliders/DoubleSliderSelector.vue +30 -24
  54. package/src/components/sliders/VerticalSliderSelector.vue +25 -21
  55. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +72 -0
  56. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +61 -0
  57. package/src/helpers/__tests__/layout-overlay-style.spec.ts +288 -0
  58. package/src/helpers/__tests__/model-helper.spec.ts +118 -0
  59. package/src/helpers/__tests__/utils.spec.ts +70 -0
  60. package/src/helpers/layoutOverlayStyle.ts +50 -30
  61. package/src/plugins/__tests__/index.spec.ts +19 -0
  62. package/src/plugins/__tests__/vuetify.spec.ts +8 -0
  63. package/src/plugins/index.ts +6 -4
  64. package/src/plugins/vuetify.ts +25 -8
  65. package/test/helper.ts +35 -0
  66. package/test/setup.ts +1 -0
  67. package/tsconfig.json +5 -2
  68. package/vite.config.mts +1 -0
  69. package/vitest.config.mts +44 -0
  70. package/src/components/expansion-panels/ExpansionHeaderMiniMenu.vue +0 -19
  71. package/src/helpers/models.ts +0 -69
  72. /package/src/components/{sliders/SliderSelector.vue → selectors/ValueSelector.vue} +0 -0
@@ -1,22 +1,34 @@
1
+ <!-- /* c8 ignore start */ -->
1
2
  <template>
3
+ <DemoModal v-model:modal="m_demo" />
2
4
  <v-dialog
5
+ id="cr-viewer"
3
6
  class="pa-0 ma-0 overflow-y-auto overflow-x-hidden"
4
7
  :modelValue="value"
5
8
  style="max-height: unset"
6
9
  :scrollable="false"
7
10
  fullscreen
8
11
  persistent
9
- @click:outside.prevent.stop="alterValue(false)"
10
12
  @input="alterValue"
11
13
  >
12
- <v-dialog v-model="m_closeDialog">
14
+ <v-dialog
15
+ v-model:model-value="m_closeDialog"
16
+ id="close-dialog-prompt"
17
+ data-test="closemodal"
18
+ >
13
19
  <v-card class="pa-1 ma-auto position-relative" max-width="450">
14
20
  <v-card-title>Close Viewer?</v-card-title>
15
- <v-card-text>Are you sure you want to close the Online Viewer?</v-card-text>
21
+ <v-card-text
22
+ >Are you sure you want to close the Online Viewer?</v-card-text
23
+ >
16
24
  <v-card-actions>
17
- <v-btn variant="text" color="red" @click="m_closeDialog = false"> Cancel </v-btn>
25
+ <v-btn variant="text" color="red" @click="m_closeDialog = false">
26
+ Cancel
27
+ </v-btn>
18
28
  <v-spacer />
19
- <v-btn color="primary" @click="closeModal"> Close without saving </v-btn>
29
+ <v-btn color="primary" @click="closeModal">
30
+ Close without saving
31
+ </v-btn>
20
32
  <v-btn color="primary" @click="closeModal"> Save Session </v-btn>
21
33
  </v-card-actions>
22
34
  </v-card>
@@ -27,7 +39,12 @@
27
39
  height="100vh"
28
40
  >
29
41
  <v-toolbar dense height="48">
30
- <v-menu :close-on-content-click="false" :close-on-click="true" open-on-hover offset-y>
42
+ <v-menu
43
+ :close-on-content-click="false"
44
+ :close-on-click="true"
45
+ open-on-hover
46
+ offset-y
47
+ >
31
48
  <template #activator="{ props, isActive }">
32
49
  <v-btn
33
50
  v-bind="props"
@@ -38,61 +55,80 @@
38
55
  <template #prepend>
39
56
  <v-icon>description</v-icon>
40
57
  </template>
41
- File
58
+ File
42
59
  </v-btn>
43
60
  </template>
44
61
  <v-card class="">
45
62
  <v-list>
46
- <v-list-item disabled>
47
- <template v-slot:prepend>
48
- <v-icon> upload </v-icon>
49
- </template>
50
- <v-list-item-title
51
- >Load New DICOM Series&nbsp;<v-chip x-small color="success">Coming Soon</v-chip></v-list-item-title
52
- >
53
- </v-list-item>
54
- <!-- <v-list-item @click="downloadDcm">-->
55
- <!-- <v-list-item-icon><v-icon> download </v-icon></v-list-item-icon>-->
56
- <!-- <v-list-item-title>Download DICOM Series</v-list-item-title>-->
57
- <!-- </v-list-item>-->
58
- <v-list-item disabled>
59
- <template v-slot:prepend>
60
- <v-icon> sync </v-icon>
61
- </template>
62
- <v-list-item-title
63
- >Load Saved Session&nbsp;<v-chip x-small color="success">Coming Soon</v-chip></v-list-item-title
64
- >
65
- </v-list-item>
66
- <v-list-item disabled>
67
- <template v-slot:prepend>
68
- <v-icon> share </v-icon>
69
- </template>
70
- <v-list-item-title>Share&nbsp;<v-chip x-small color="success">Coming Soon</v-chip></v-list-item-title>
71
- </v-list-item>
72
- <v-list-item @click="alterValue(false)">
73
- <template v-slot:prepend>
74
- <v-icon> close </v-icon>
75
- </template>
76
- <v-list-item-title>Close Viewer</v-list-item-title>
77
- </v-list-item>
63
+ <v-list-item @click="executeOption('OnLoadNewDicomSeries')">
64
+ <template v-slot:prepend>
65
+ <v-icon> upload </v-icon>
66
+ </template>
67
+ <v-list-item-title>Load New DICOM Series</v-list-item-title>
68
+ </v-list-item>
69
+ <v-list-item @click="executeOption('OnDownloadDicomSeries')">
70
+ <template v-slot:prepend>
71
+ <v-icon> download </v-icon>
72
+ </template>
73
+ <v-list-item-title>Download DICOM Series</v-list-item-title>
74
+ </v-list-item>
75
+ <v-list-item @click="executeOption('OnLoadSavedSession')">
76
+ <template v-slot:prepend>
77
+ <v-icon> sync </v-icon>
78
+ </template>
79
+ <v-list-item-title> Load Saved Session </v-list-item-title>
80
+ </v-list-item>
81
+ <v-list-item @click="executeOption('OnShare')">
82
+ <template v-slot:prepend>
83
+ <v-icon> share </v-icon>
84
+ </template>
85
+ <v-list-item-title>Share</v-list-item-title>
86
+ </v-list-item>
87
+ <v-list-item
88
+ @click="
89
+ executeOption('OnClosePopup');
90
+ alterValue(false);
91
+ "
92
+ >
93
+ <template v-slot:prepend>
94
+ <v-icon> close </v-icon>
95
+ </template>
96
+ <v-list-item-title>Close Viewer</v-list-item-title>
97
+ </v-list-item>
78
98
  </v-list>
79
99
  </v-card>
80
100
  </v-menu>
81
- <v-menu :close-on-content-click="false" :close-on-click="true" open-on-hover offset-y>
101
+ <v-menu
102
+ :close-on-content-click="false"
103
+ :close-on-click="true"
104
+ open-on-hover
105
+ offset-y
106
+ >
82
107
  <template #activator="{ props, isActive }">
83
108
  <v-btn
84
109
  variant="flat"
85
- v-bind="props" :color="isActive ? 'secondary' : 'primary'">
110
+ v-bind="props"
111
+ :color="isActive ? 'secondary' : 'primary'"
112
+ >
86
113
  <template #prepend>
87
114
  <v-icon>settings</v-icon>
88
115
  </template>
89
- Settings
116
+ Settings
90
117
  </v-btn>
91
118
  </template>
92
- <v-card min-width="400" class=" pa-4">
93
- <SliderSelector v-model:value="scanState.Display.Brightness" label="Adjust Brightness" />
94
- <SliderSelector v-model:value="scanState.Display.Contrast" label="Adjust Contrast" />
95
- <SliderSelector v-model:value="scanState.Display.Opacity" label="Adjust Opacity" />
119
+ <v-card min-width="400" class="pa-4">
120
+ <SliderSelector
121
+ v-model:value="scanState.Display.Brightness"
122
+ label="Adjust Brightness"
123
+ />
124
+ <SliderSelector
125
+ v-model:value="scanState.Display.Contrast"
126
+ label="Adjust Contrast"
127
+ />
128
+ <SliderSelector
129
+ v-model:value="scanState.Display.Opacity"
130
+ label="Adjust Opacity"
131
+ />
96
132
  <v-divider class="my-4" />
97
133
  <SliderSelector
98
134
  v-model:value="scanState.InteractionSettings.PanSensivitity"
@@ -113,7 +149,9 @@
113
149
  label="Rotate Sensitivity"
114
150
  />
115
151
  <SliderSelector
116
- v-model:value="scanState.InteractionSettings.CameraRotateSensitivity"
152
+ v-model:value="
153
+ scanState.InteractionSettings.CameraRotateSensitivity
154
+ "
117
155
  :min="0"
118
156
  :max="100"
119
157
  label="Camera Rotate Sensitivity"
@@ -122,7 +160,9 @@
122
160
  </v-menu>
123
161
 
124
162
  <v-spacer />
125
- <div class="font-weight-bold">Not for Diagnostic Use</div>
163
+ <div class="font-weight-bold">
164
+ <span v-if="isDemo">[DEMO]&nbsp;</span>Not for Diagnostic Use
165
+ </div>
126
166
  <v-spacer />
127
167
  <v-btn
128
168
  class="ma-1 mr-0 pa-1"
@@ -140,7 +180,9 @@
140
180
  @click="layouts('lo_03')"
141
181
  ><v-icon style="rotate: -90deg">view_comfy</v-icon></v-btn
142
182
  >
143
- <v-btn class="" variant="flat" color="red" @click="alterValue(false)">Close Viewer</v-btn>
183
+ <v-btn class="" variant="flat" color="red" @click="alterValue(false)"
184
+ >Close Viewer</v-btn
185
+ >
144
186
  </v-toolbar>
145
187
  <v-navigation-drawer
146
188
  v-model="drawer"
@@ -154,7 +196,11 @@
154
196
  width="300"
155
197
  >
156
198
  <template v-slot:prepend>
157
- <div class="d-flex align-center pb-1" :class="drawerCollapsed ? 'py-2' : 'pa-2'" @click="snap">
199
+ <div
200
+ class="d-flex align-center pb-1"
201
+ :class="drawerCollapsed ? 'py-2' : 'pa-2'"
202
+ @click="snap"
203
+ >
158
204
  <img
159
205
  v-if="!drawerCollapsed"
160
206
  src="../../assets/images/dark/3dicom-logo.svg"
@@ -197,14 +243,24 @@
197
243
  <div
198
244
  :class="[drawerCollapsed && 'mx-auto']"
199
245
  class="mx-2"
200
-
201
- :style="drawerCollapsed && 'margin-left: 10px !important; margin-right: 10px !important;'">
202
- <v-icon :large="drawerCollapsed" :size="drawerCollapsed ? 32 : 24" :color="item.color">
203
- {{ item.icon || 'radio_button_checked' }}
246
+ :style="
247
+ drawerCollapsed &&
248
+ 'margin-left: 10px !important; margin-right: 10px !important;'
249
+ "
250
+ >
251
+ <v-icon
252
+ :large="drawerCollapsed"
253
+ :size="drawerCollapsed ? 32 : 24"
254
+ :color="item.color"
255
+ >
256
+ {{ item.icon || "radio_button_checked" }}
204
257
  </v-icon>
205
258
  </div>
206
259
  </template>
207
- <v-list-item-title v-if="!drawerCollapsed" class="text-white font-weight-medium ml-3">
260
+ <v-list-item-title
261
+ v-if="!drawerCollapsed"
262
+ class="text-white font-weight-medium ml-3"
263
+ >
208
264
  {{ item.text }}
209
265
  </v-list-item-title>
210
266
  </v-list-item>
@@ -225,9 +281,16 @@
225
281
  drawerCollapsed = false;
226
282
  "
227
283
  >
228
- <v-icon size="36" color="white" style="margin-left: 8px !important; margin-right: 10px !important;">
229
- {{ item.icon || 'radio_button_checked' }}
230
- </v-icon>
284
+ <v-icon
285
+ size="36"
286
+ color="white"
287
+ style="
288
+ margin-left: 8px !important;
289
+ margin-right: 10px !important;
290
+ "
291
+ >
292
+ {{ item.icon || "radio_button_checked" }}
293
+ </v-icon>
231
294
  </v-list-item>
232
295
  </template>
233
296
  {{ item.tooltip }}
@@ -239,24 +302,35 @@
239
302
  style="max-height: 95vh"
240
303
  theme="dark"
241
304
  accordion
242
- class="mt-1 transparent pr-0 mr-0"
305
+ class="mt-1 transparent pr-0 mr-0"
243
306
  >
244
307
  <v-expansion-panel class="transparent px-0">
245
- <ExpansionHeaderMiniMenu :mini-menu="miniMenu[0]" />
246
- <v-expansion-panel-text class="px-0" >
308
+ <v-expansion-panel-title class="font-weight-bold transparent">
309
+ <span
310
+ ><v-icon small>{{ miniMenu[0].icon }}</v-icon
311
+ >&nbsp;&nbsp;{{ miniMenu[0].text }}</span
312
+ >
313
+ <v-spacer />
314
+ </v-expansion-panel-title>
315
+ <v-expansion-panel-text class="px-0">
247
316
  <DoubleSliderSelector
248
317
  v-model:value="windowSlider"
249
318
  label="Skin to Bone"
250
319
  :min="huMinMax.min"
251
320
  :max="huMinMax.max"
252
321
  />
253
- <DoubleSliderSelector v-model:value="thresholdSlider" label="Fine Adjustment" v-bind="huMinMax" />
322
+ <DoubleSliderSelector
323
+ v-model:value="thresholdSlider"
324
+ label="Fine Adjustment"
325
+ v-bind="huMinMax"
326
+ />
254
327
  <v-card-actions class="py-3">
255
328
  <v-select
256
329
  :value="getCurrentGreyscalePreset()"
257
330
  :items="initialScanState.GreyscalePresets"
258
331
  label="Greyscale Preset"
259
332
  item-text="Name"
333
+ theme="light"
260
334
  density="compact"
261
335
  variant="outlined"
262
336
  persistent-placeholder
@@ -266,7 +340,7 @@
266
340
  closeOnContentClick: true,
267
341
  }"
268
342
  placeholder="Select a Density Preset"
269
- @change="setPreset(PresetActions.pr01, $event)"
343
+ @change="setPreset(PresetsActions.pr01, $event)"
270
344
  >
271
345
  <template #item="{ props, item }">
272
346
  <v-list-item
@@ -274,22 +348,27 @@
274
348
  ripple
275
349
  :title="item.raw.Name"
276
350
  lines="three"
277
-
278
351
  @mousedown.prevent
279
- @click="setPreset(PresetActions.pr01, item.raw)"
352
+ @click="setPreset(PresetsActions.pr01, item.raw)"
280
353
  >
281
354
  <template v-slot:prepend>
282
- <v-icon :icon="getIconForPreset(item.raw.Name)"></v-icon>
355
+ <v-icon
356
+ :icon="getIconForPreset(item.raw.Name)"
357
+ ></v-icon>
283
358
  </template>
284
359
  <template v-slot:subtitle>
285
- Skin Density: <span class="text-mono">{{ item.raw.Lower }}</span>
360
+ Skin Density:
361
+ <span class="text-mono">{{ item.raw.Lower }}</span>
286
362
  <v-spacer />
287
- Bone Density: <span class="text-mono">{{ item.raw.Upper }}</span>
363
+ Bone Density:
364
+ <span class="text-mono">{{ item.raw.Upper }}</span>
288
365
  </template>
289
366
  </v-list-item>
290
367
  </template>
291
368
  <template v-slot:selection="{ item }">
292
- <span v-if="item.raw === undefined || !item.raw.Name">None</span>
369
+ <span v-if="item.raw === undefined || !item.raw.Name"
370
+ >None</span
371
+ >
293
372
  <span v-else>{{ item.raw.Name }}</span>
294
373
  </template>
295
374
  </v-select>
@@ -308,12 +387,11 @@
308
387
  }"
309
388
  placeholder="Select a Colour Preset"
310
389
  return-object
311
- @update:modelValue="setPreset(PresetActions.pr02, $event)"
390
+ @update:modelValue="setPreset(PresetsActions.pr02, $event)"
312
391
  ></v-select>
313
392
  </v-card-actions>
314
393
  </v-expansion-panel-text>
315
394
  </v-expansion-panel>
316
-
317
395
  </v-expansion-panels>
318
396
  </v-navigation-drawer>
319
397
 
@@ -333,15 +411,21 @@
333
411
  :style="drawerCollapsed ? 'left: 68px' : 'left: 300px'"
334
412
  @click="drawerCollapsed = !drawerCollapsed"
335
413
  >
336
- <v-icon>{{ drawerCollapsed ? 'chevron_right' : 'chevron_left' }}</v-icon>
414
+ <v-icon>{{
415
+ drawerCollapsed ? "chevron_right" : "chevron_left"
416
+ }}</v-icon>
337
417
  </v-btn>
338
418
 
339
- <div class="position-relative pa-0" :style="drawerCollapsed ? 'margin-left: 68px' : 'margin-left: 300px'">
419
+ <div
420
+ class="position-relative pa-0"
421
+ :style="drawerCollapsed ? 'margin-left: 68px' : 'margin-left: 300px'"
422
+ >
340
423
  <WebGL3DR
341
424
  v-show="instanceLoaded && !scanLoading"
342
425
  :class="!(instanceLoaded && !scanLoading) && 'no-pointer-events'"
343
426
  v-if="value"
344
427
  ref="web_gl"
428
+ id="webgl-container"
345
429
  @on_payload="handleOnPayload"
346
430
  @instance_loaded="load"
347
431
  @hover="hoverOverCanvas"
@@ -352,140 +436,195 @@
352
436
  :key="layout.Anchor"
353
437
  :style="{
354
438
  ...generateDivStyleForLayout(layout),
355
- cursor: getCurrentActiveView(layout) === ScanView.Volume ? 'grab !important' : 'default',
439
+ cursor:
440
+ getCurrentActiveView(layout) === ScanView.Volume
441
+ ? 'grab !important'
442
+ : 'default',
356
443
  }"
444
+ :data-box-internal="layout.DefaultView"
357
445
  >
358
446
  <v-hover>
359
447
  <template v-slot:default="{ isHovering, props }">
360
- <div style="width: 100%; height: 100%" v-bind="props">
361
- <div class="buttons-in-view" v-show="isHovering" >
362
-
363
- <div v-if="scanState.Layout.PositionData.length !== 1">
364
- <v-btn
365
- color="transparent"
366
- @click="fullscreenLayout(layout.DefaultView)"
367
- :icon="true"
448
+ <div style="width: 100%; height: 100%" v-bind="props">
449
+ <div class="buttons-in-view" v-show="isHovering">
450
+ <div v-if="scanState.Layout.PositionData.length !== 1">
451
+ <v-btn
452
+ color="transparent"
453
+ @click="fullscreenLayout(layout.DefaultView)"
454
+ :icon="true"
455
+ >
456
+ <v-icon color="white">fullscreen</v-icon>
457
+ </v-btn>
458
+ <v-tooltip
459
+ target="cursor"
460
+ location="top"
461
+ activator="parent"
462
+ >
463
+ Make
464
+ {{ getViewName(getCurrentActiveView(layout)) }}
465
+ fullscreen
466
+ </v-tooltip>
467
+ </div>
468
+ <div v-if="scanState.Layout.PositionData.length === 1">
469
+ <v-btn
470
+ color="transparent"
471
+ @click="layouts(previousLayout)"
472
+ :icon="true"
473
+ >
474
+ <v-icon color="white">fullscreen_exit</v-icon>
475
+ </v-btn>
476
+ <v-tooltip
477
+ target="cursor"
478
+ location="top"
479
+ activator="parent"
480
+ >
481
+ Exit Fullscreen View
482
+ </v-tooltip>
483
+ </div>
484
+ <div
485
+ v-if="getCurrentActiveView(layout) === ScanView.Volume"
368
486
  >
369
- <v-icon color="white">fullscreen</v-icon>
370
-
371
- </v-btn>
372
- <v-tooltip target="cursor" location="top" activator="parent">
373
- Make {{ getViewName(getCurrentActiveView(layout)) }} fullscreen
374
- </v-tooltip>
375
- </div>
376
- <div v-if="scanState.Layout.PositionData.length === 1">
377
- <v-btn
378
- color="transparent"
379
- @click="layouts(previousLayout)"
380
- :icon="true"
487
+ <v-btn
488
+ color="transparent"
489
+ :icon="true"
490
+ @click="
491
+ viewSelection('vs_05');
492
+ viewSelection('vs_06');
493
+ "
494
+ ><v-icon color="white">home</v-icon></v-btn
495
+ >
496
+ <v-tooltip
497
+ target="cursor"
498
+ location="top"
499
+ activator="parent"
500
+ >
501
+ Reset volume to default view
502
+ </v-tooltip>
503
+ </div>
504
+ <div
505
+ v-if="getCurrentActiveView(layout) === ScanView.Volume"
381
506
  >
382
- <v-icon color="white">fullscreen_exit</v-icon>
383
- </v-btn>
384
- <v-tooltip target="cursor" location="top" activator="parent">
385
- Exit Fullscreen View
386
- </v-tooltip>
387
- </div>
388
- <div v-if="getCurrentActiveView(layout) === ScanView.Volume">
389
- <v-btn
390
- color="transparent"
391
- :icon="true"
392
- @click="
393
- viewSelection('vs_05');
394
- viewSelection('vs_06');
395
- "
396
- ><v-icon color="white">home</v-icon></v-btn
507
+ <v-menu
508
+ :close-on-content-click="false"
509
+ :close-on-click="true"
510
+ >
511
+ <template v-slot:activator="{ props }">
512
+ <div v-bind="{ ...props }">
513
+ <v-btn color="transparent" :icon="true">
514
+ <v-icon color="white">cut</v-icon>
515
+ </v-btn>
516
+ <v-tooltip
517
+ target="cursor"
518
+ location="top"
519
+ activator="parent"
520
+ >
521
+ Slice the 3D Volume
522
+ </v-tooltip>
523
+ </div>
524
+ </template>
525
+ <v-card min-width="400" class="pb-2">
526
+ <v-card-title>Slice into the 3D Volume</v-card-title>
527
+ <DoubleSliderSelector
528
+ v-model="tSlider"
529
+ label="Transverse"
530
+ v-bind="tMinMax"
531
+ />
532
+ <DoubleSliderSelector
533
+ v-model="sSlider"
534
+ label="Sagittal"
535
+ v-bind="sMinMax"
536
+ />
537
+ <DoubleSliderSelector
538
+ v-model="cSlider"
539
+ label="Coronal"
540
+ v-bind="cMinMax"
541
+ />
542
+ </v-card>
543
+ </v-menu>
544
+ </div>
545
+ <div
546
+ v-if="getCurrentActiveView(layout) !== ScanView.Volume"
397
547
  >
398
- <v-tooltip target="cursor" location="top" activator="parent">
399
- Reset volume to default view
400
- </v-tooltip>
401
- </div>
402
- <div v-if="getCurrentActiveView(layout) === ScanView.Volume">
403
- <v-menu :close-on-content-click="false" :close-on-click="true">
404
- <template v-slot:activator="{ props }">
405
- <div v-bind="{ ...props }"
406
- >
407
- <v-btn
408
- color="transparent"
409
- :icon="true"
410
- >
411
- <v-icon color="white">cut</v-icon>
412
- </v-btn>
413
- <v-tooltip target="cursor" location="top" activator="parent">
414
- Slice the 3D Volume
548
+ <v-menu
549
+ :close-on-content-click="false"
550
+ :close-on-click="true"
551
+ offset-overflow
552
+ top
553
+ >
554
+ <template v-slot:activator="{ props }">
555
+ <v-tooltip top>
556
+ <template #activator="{ props: ttprops }">
557
+ <v-btn
558
+ v-bind="{ ...props, ...ttprops }"
559
+ icon
560
+ class="icon transparent"
561
+ >
562
+ <v-icon color="white">360</v-icon>
563
+ </v-btn>
564
+ </template>
565
+ Rotate
566
+ {{ getViewName(getCurrentActiveView(layout)) }} by
567
+ an angle
415
568
  </v-tooltip>
416
- </div>
417
- </template>
418
- <v-card min-width="400" class="pb-2">
419
- <v-card-title>Slice into the 3D Volume</v-card-title>
420
- <DoubleSliderSelector v-model="tSlider" label="Transverse" v-bind="tMinMax" />
421
- <DoubleSliderSelector v-model="sSlider" label="Sagittal" v-bind="sMinMax" />
422
- <DoubleSliderSelector v-model="cSlider" label="Coronal" v-bind="cMinMax" />
423
- </v-card>
424
- </v-menu>
425
- </div>
426
- <div v-if="getCurrentActiveView(layout) !== ScanView.Volume">
427
- <v-menu :close-on-content-click="false" :close-on-click="true" offset-overflow top>
428
- <template v-slot:activator="{ props }">
429
- <v-tooltip top>
430
- <template #activator="{ props: ttprops}">
569
+ </template>
570
+ <v-card min-width="200" width="200" class="pb-2">
571
+ <v-card-subtitle
572
+ >Rotate
573
+ {{ getViewName(getCurrentActiveView(layout)) }} by
574
+ an angle</v-card-subtitle
575
+ >
576
+ <v-card-text class="py-0">
577
+ <v-text-field
578
+ hide-details
579
+ outlined
580
+ v-model="rotationDeg"
581
+ type="number"
582
+ suffix="deg"
583
+ ></v-text-field>
584
+ </v-card-text>
585
+ <v-card-actions>
586
+ <v-spacer />
431
587
  <v-btn
432
- v-bind="{ ...props, ...ttprops }"
433
- icon
434
- class="icon transparent"
588
+ color="primary"
589
+ @click="
590
+ rotateByDeg(
591
+ getCurrentActiveView(layout),
592
+ rotationDeg
593
+ )
594
+ "
595
+ >Rotate</v-btn
435
596
  >
436
- <v-icon color="white">360</v-icon>
437
- </v-btn>
438
- </template>
439
- Rotate {{ getViewName(getCurrentActiveView(layout)) }} by an angle
440
- </v-tooltip>
441
- </template>
442
- <v-card min-width="200" width="200" class="pb-2">
443
- <v-card-subtitle
444
- >Rotate {{ getViewName(getCurrentActiveView(layout)) }} by an angle</v-card-subtitle
445
- >
446
- <v-card-text class="py-0">
447
- <v-text-field
448
- hide-details
449
- outlined
450
- v-model="rotationDeg"
451
- type="number"
452
- suffix="deg"
453
- ></v-text-field>
454
- </v-card-text>
455
- <v-card-actions>
456
- <v-spacer />
457
- <v-btn color="primary" @click="rotateByDeg(getCurrentActiveView(layout), rotationDeg)"
458
- >Rotate</v-btn
459
- >
460
- </v-card-actions>
461
- </v-card>
462
- </v-menu>
597
+ </v-card-actions>
598
+ </v-card>
599
+ </v-menu>
600
+ </div>
601
+ </div>
602
+ <div class="slider-in-view" v-if="isHovering">
603
+ <VerticalSliderSelector
604
+ v-if="
605
+ getCurrentActiveView(layout) === ScanView.Transverse
606
+ "
607
+ v-model:value="scanState.Orientations.Transverse.Slice"
608
+ v-bind="tMinMax"
609
+ />
610
+ <VerticalSliderSelector
611
+ v-if="getCurrentActiveView(layout) === ScanView.Coronal"
612
+ v-model:value="scanState.Orientations.Coronal.Slice"
613
+ v-bind="cMinMax"
614
+ />
615
+ <VerticalSliderSelector
616
+ v-if="getCurrentActiveView(layout) === ScanView.Sagittal"
617
+ v-model:value="scanState.Orientations.Sagittal.Slice"
618
+ v-bind="sMinMax"
619
+ />
463
620
  </div>
464
- </div>
465
- <div class="slider-in-view" v-if="isHovering">
466
- <VerticalSliderSelector
467
- v-if="getCurrentActiveView(layout) === ScanView.Transverse"
468
- v-model:value="scanState.Orientations.Transverse.Slice"
469
- v-bind="tMinMax"
470
- />
471
- <VerticalSliderSelector
472
- v-if="getCurrentActiveView(layout) === ScanView.Coronal"
473
- v-model:value="scanState.Orientations.Coronal.Slice"
474
- v-bind="cMinMax"
475
- />
476
- <VerticalSliderSelector
477
- v-if="getCurrentActiveView(layout) === ScanView.Sagittal"
478
- v-model:value="scanState.Orientations.Sagittal.Slice"
479
- v-bind="sMinMax"
480
- />
481
- </div>
482
621
 
483
- <div class="top-lhc" v-if="isHovering">
484
- <div class="white--text">
485
- {{ getViewName(getCurrentActiveView(layout)) }}
622
+ <div class="top-lhc" v-if="isHovering">
623
+ <div class="white--text">
624
+ {{ getViewName(getCurrentActiveView(layout)) }}
625
+ </div>
486
626
  </div>
487
627
  </div>
488
- </div>
489
628
  </template>
490
629
  </v-hover>
491
630
  </div>
@@ -493,10 +632,16 @@
493
632
  </div>
494
633
 
495
634
  <LoadingSpinner v-if="!instanceLoaded" />
496
- <LoadingSpinner v-if="scanLoading" text="Rendering your scan in <span class='sub-type'>3DICOM</span>" />
635
+ <LoadingSpinner v-if="scanLoading" text="Rendering your scan in 3D" />
497
636
  <v-textarea
498
637
  v-if="stateOverlay"
499
- style="position: absolute; top: 0px; right: -0px; width: 240px; z-index: 10000"
638
+ style="
639
+ position: absolute;
640
+ top: 0px;
641
+ right: -0px;
642
+ width: 240px;
643
+ z-index: 10000;
644
+ "
500
645
  class="text--white"
501
646
  color="white"
502
647
  dark
@@ -508,66 +653,115 @@
508
653
  </v-card>
509
654
  </v-dialog>
510
655
  </template>
511
- <script setup lang="ts" >
656
+ <!-- /* c8 ignore stop */ -->
657
+ <script setup lang="ts">
658
+ import LoadingSpinner from "@/components/loading/LoadingSpinner.vue";
659
+ import WebGL3DR from "@/components/WebGL3DR.vue";
660
+ import DoubleSliderSelector from "@/components/sliders/DoubleSliderSelector.vue";
661
+ import SliderSelector from "@/components/selectors/ValueSelector.vue";
662
+ import VerticalSliderSelector from "@/components/sliders/VerticalSliderSelector.vue";
512
663
 
513
- import ExpansionHeaderMiniMenu from '@/components/expansion-panels/ExpansionHeaderMiniMenu.vue';
514
- import LoadingSpinner from '@/components/loading/LoadingSpinner.vue';
515
- import WebGL3DR from '@/components/WebGL3DR.vue';
516
- import DoubleSliderSelector from '@/components/sliders/DoubleSliderSelector.vue';
517
- import SliderSelector from '@/components/sliders/SliderSelector.vue';
518
- import VerticalSliderSelector from '@/components/sliders/VerticalSliderSelector.vue';
664
+ import { generateDivStyleForLayout } from "@/helpers/layoutOverlayStyle";
519
665
 
520
- import { generateDivStyleForLayout } from '@/helpers/layoutOverlayStyle';
521
666
  import {
522
- SliderActions,
523
- PresetActions,
667
+ inflateInitialScanState,
668
+ inflateScanState,
669
+ } from "@/helpers/modelHelper";
670
+ import {
671
+ SlidersActions,
672
+ PresetsActions,
524
673
  ScanMovementActions,
525
674
  ScanOrientationActions,
526
675
  FileManagementActions,
527
676
  FrontEndInterfaces,
528
- } from '@/helpers/models';
529
- import {
530
- inflateInitialScanState,
531
- inflateScanState,
532
- } from '../../helpers/modelHelper';
533
- import {
534
677
  AnchorPoint,
535
678
  ColourPresetData,
536
679
  CurrentScanState,
537
680
  InitialScanState,
538
681
  PositionData,
539
682
  ScanView,
540
- } from '@3cr/types-ts';
683
+ InteractivityActions,
684
+ NotificationsActions,
685
+ NotificationPayload,
686
+ } from "@3cr/types-ts";
541
687
 
542
- import { toNumber } from '@/helpers/utils';
543
- import {computed, ref, unref, watch, defineEmits, nextTick} from "vue";
544
- import {LoadViewerPayload} from "../../../index";
688
+ import { toNumber } from "@/helpers/utils";
689
+ import { computed, ref, unref, watch, defineEmits, nextTick } from "vue";
690
+ import {
691
+ defaultOptions,
692
+ LoadViewerOptions,
693
+ LoadViewerPayload,
694
+ } from "../../../index";
545
695
 
546
696
  const emit = defineEmits<{
547
697
  instanceLoaded: [void];
698
+ snap: [void];
548
699
  }>();
549
700
 
550
- defineExpose({
551
- alterValue,
552
- load
553
- });
554
-
555
701
  export interface Props {
556
- payload: LoadViewerPayload;
702
+ payload?: LoadViewerPayload;
703
+ options?: LoadViewerOptions;
557
704
  }
558
- const componentKey = ref(0);
559
705
 
560
706
  const props = withDefaults(defineProps<Props>(), {
561
707
  payload: () => ({
562
- Url:"https://webgl-3dr.singular.health/test_scans/8bdddee1-e581-485d-827d-6aa12eef2fc8/Head+Axial+Axial.3vxl",
563
- DecryptionKey:{
564
- Iv:"x856FgjpYDsRhIa3BFj5cg==",
565
- Key:"OWjSMiL/ewUV1V6fGybhKcTyiysTPsIMp2DjdVoOUGI="
566
- }
567
- })
708
+ Url: "https://webgl-3dr.singular.health/test_scans/8bdddee1-e581-485d-827d-6aa12eef2fc8/Head+Axial+Axial.3vxl",
709
+ DecryptionKey: {
710
+ Iv: "x856FgjpYDsRhIa3BFj5cg==",
711
+ Key: "OWjSMiL/ewUV1V6fGybhKcTyiysTPsIMp2DjdVoOUGI=",
712
+ },
713
+ }),
714
+ options: () => defaultOptions,
568
715
  });
716
+ const isDemo = ref<boolean>(false);
717
+ let m_demo = ref<boolean>(false);
718
+
719
+ const demoOptions: LoadViewerOptions = {
720
+ OnClosePopup: () => Promise.resolve(),
721
+ OnExitViewer: () => {
722
+ m_demo.value = true;
723
+ },
724
+ OnLoadNewDicomSeries: () => {
725
+ m_demo.value = true;
726
+ },
727
+ OnDownloadDicomSeries: () => {
728
+ m_demo.value = true;
729
+ },
730
+ OnLoadSavedSession: () => {
731
+ m_demo.value = true;
732
+ },
733
+ OnSaveSession: () => {
734
+ m_demo.value = true;
735
+ },
736
+ OnSendTo3rdParty: () => {
737
+ m_demo.value = true;
738
+ },
739
+ OnShareToMobile: () => {
740
+ m_demo.value = true;
741
+ },
742
+ OnShare: () => {
743
+ m_demo.value = true;
744
+ },
745
+ OnScreenshot: () => {
746
+ m_demo.value = true;
747
+ },
748
+ };
749
+
750
+ function executeOption(key: keyof LoadViewerOptions) {
751
+ if (unref(isDemo)) {
752
+ const functionToExecute = demoOptions[key];
753
+ if (functionToExecute !== undefined) {
754
+ functionToExecute();
755
+ }
756
+ } else {
757
+ const functionToExecute = unref(props.options)[key];
758
+ if (functionToExecute !== undefined) {
759
+ functionToExecute();
760
+ }
761
+ }
762
+ }
569
763
 
570
- const emptyPayload = { Version: '1.1.0' };
764
+ const emptyPayload = { Version: "1.1.0" };
571
765
 
572
766
  const value = ref<boolean>(false);
573
767
  const drawer = ref<boolean>(true);
@@ -578,222 +772,295 @@ const scanState = ref<CurrentScanState>(inflateScanState());
578
772
  const initialScanState = ref<InitialScanState>(inflateInitialScanState());
579
773
  const currentColourPreset = ref<ColourPresetData | undefined>(undefined);
580
774
  const openPanels = ref<number>(0);
581
- const previousLayout = ref<string>('lo_02');
775
+ const previousLayout = ref<string>("lo_02");
582
776
  const footerItems = ref([
583
777
  {
584
- text: 'Reset Scan',
585
- icon: 'refresh',
586
- color: 'red',
778
+ text: "Reset Scan",
779
+ icon: "refresh",
780
+ color: "red",
587
781
  click: async () => {
588
- console.log('Reset Scan');
589
- await viewSelection('vs_05');
590
- await viewSelection('vs_06');
782
+ console.log("Reset Scan");
783
+ await viewSelection("vs_05");
784
+ await viewSelection("vs_06");
591
785
  },
592
786
  },
593
787
  {
594
- text: 'Send to 3rd Party',
595
- icon: 'send',
596
- color: 'blue',
597
- click: () => alterValue(false),
788
+ text: "Send to 3rd Party",
789
+ icon: "send",
790
+ color: "blue",
791
+ click: async () => executeOption("OnSendTo3rdParty"),
598
792
  },
599
793
  {
600
- text: 'Share to Mobile / VR',
601
- icon: 'share',
602
- color: 'yellow',
603
- click: () => alterValue(false),
794
+ text: "Share to Mobile / VR",
795
+ icon: "share",
796
+ color: "yellow",
797
+ click: async () => executeOption("OnShareToMobile"),
604
798
  },
605
799
  {
606
- text: 'Screenshot View',
607
- icon: 'screenshot_region',
608
- color: 'green',
609
- click: () => {
610
- snap();
611
- },
800
+ text: "Screenshot View",
801
+ icon: "screenshot_region",
802
+ color: "green",
803
+ click: async () => executeOption("OnScreenshot"),
612
804
  },
613
805
  ]);
614
806
 
615
807
  const miniMenu = ref([
616
808
  {
617
- icon: 'display_settings',
618
- text: 'Tissue Density',
809
+ icon: "display_settings",
810
+ text: "Tissue Density",
619
811
  tooltip:
620
- 'Tissue Density - Change the view of density within the scan, allowing you to see through a certain density of particles',
812
+ "Tissue Density - Change the view of density within the scan, allowing you to see through a certain density of particles",
621
813
  },
622
814
  // { icon: 'display_settings', text: 'Display', tooltip: 'Display - Alter the view of the current scan' },
623
- ])
815
+ ]);
624
816
 
625
817
  const huMinMax = ref({
626
818
  min: -999999,
627
819
  max: 999999,
628
- })
820
+ });
629
821
 
630
822
  const tMinMax = ref({
631
823
  min: 0,
632
824
  max: 999999,
633
- })
825
+ });
634
826
  const sMinMax = ref({
635
827
  min: 0,
636
828
  max: 999999,
637
- })
829
+ });
638
830
  const cMinMax = ref({
639
831
  min: 0,
640
832
  max: 999999,
641
- })
642
- const rotationDeg = ref<number>(0)
643
- const scanStateIncoming = ref<string>('')
644
- const transactionStarted = ref<boolean>(false)
645
- const stateOverlay = ref<boolean>(false)
646
- const m_closeDialog = ref<boolean>(false)
647
-
648
- watch(currentColourPreset, (val) => {console.log(val)})
833
+ });
834
+ const rotationDeg = ref<number>(0);
835
+ const scanStateIncoming = ref<string>("");
836
+ const transactionStarted = ref<boolean>(false);
837
+ const stateOverlay = ref<boolean>(false);
838
+ const m_closeDialog = ref<boolean>(false);
839
+
649
840
  const windowSlider = computed({
650
841
  get() {
651
- return [unref(scanState).Display.WindowLower, unref(scanState).Display.WindowUpper];
842
+ return [
843
+ unref(scanState).Display.WindowLower,
844
+ unref(scanState).Display.WindowUpper,
845
+ ];
652
846
  },
653
847
  set(value: Array<number>) {
654
848
  scanState.value.Display.WindowLower = value[0];
655
849
  scanState.value.Display.WindowUpper = value[1];
656
- }
657
- })
850
+ },
851
+ });
658
852
  const thresholdSlider = computed({
659
853
  get() {
660
- return [Math.trunc(unref(scanState).Display.ThresholdLower), Math.trunc(unref(scanState).Display.ThresholdUpper)];
854
+ return [
855
+ Math.trunc(unref(scanState).Display.ThresholdLower),
856
+ Math.trunc(unref(scanState).Display.ThresholdUpper),
857
+ ];
661
858
  },
662
859
  set(value: Array<number>) {
663
860
  scanState.value.Display.ThresholdLower = value[0];
664
861
  scanState.value.Display.ThresholdUpper = value[1];
665
- }
666
- })
862
+ },
863
+ });
667
864
  const tSlider = computed({
668
865
  get() {
669
- return [Math.trunc(unref(scanState).Slice.TransverseLower), Math.trunc(unref(scanState).Slice.TransverseUpper)];
866
+ return [
867
+ Math.trunc(unref(scanState).Slice.TransverseLower),
868
+ Math.trunc(unref(scanState).Slice.TransverseUpper),
869
+ ];
670
870
  },
671
871
  set(value: Array<number>) {
672
872
  scanState.value.Slice.TransverseLower = value[0];
673
873
  scanState.value.Slice.TransverseLower = value[1];
674
- }
675
- })
874
+ },
875
+ });
676
876
  const sSlider = computed({
677
877
  get() {
678
- return [Math.trunc(unref(scanState).Slice.SagittalLower), Math.trunc(unref(scanState).Slice.SagittalUpper)];
878
+ return [
879
+ Math.trunc(unref(scanState).Slice.SagittalLower),
880
+ Math.trunc(unref(scanState).Slice.SagittalUpper),
881
+ ];
679
882
  },
680
883
  set(value: Array<number>) {
681
884
  scanState.value.Slice.SagittalLower = value[0];
682
885
  scanState.value.Slice.SagittalUpper = value[1];
683
- }
684
- })
886
+ },
887
+ });
685
888
 
686
889
  const cSlider = computed({
687
890
  get() {
688
- return [Math.trunc(unref(scanState).Slice.CoronalLower), Math.trunc(unref(scanState).Slice.CoronalUpper)];
891
+ return [
892
+ Math.trunc(unref(scanState).Slice.CoronalLower),
893
+ Math.trunc(unref(scanState).Slice.CoronalUpper),
894
+ ];
689
895
  },
690
896
  set(value: Array<number>) {
691
897
  scanState.value.Slice.CoronalLower = value[0];
692
898
  scanState.value.Slice.CoronalUpper = value[1];
693
- }
694
- })
899
+ },
900
+ });
695
901
  const web_gl = ref<typeof WebGL3DR | null>(null);
696
902
 
697
-
698
- watch(() => scanState.value.Display.Brightness, async (value: number) => {
699
- await sliderHandler(SliderActions.sl01, value);
700
- })
701
- watch(() => scanState.value.Display.Contrast, async (value: number) => {
702
- await sliderHandler(SliderActions.sl02, value);
703
- })
704
- watch(() => scanState.value.Display.Opacity, async (value: number) => {
705
- await sliderHandler(SliderActions.sl03, value);
706
- })
707
- watch(() => scanState.value.Display.WindowLower, async (value: number) => {
708
- await sliderHandler(SliderActions.sl04, value);
709
- })
710
- watch(() => scanState.value.Display.WindowUpper, async (value: number) => {
711
- await sliderHandler(SliderActions.sl05, value);
712
- })
713
- watch(() => scanState.value.Display.ThresholdLower, async (value: number) => {
714
- await sliderHandler(SliderActions.sl06, value);
715
- })
716
- watch(() => scanState.value.Display.ThresholdUpper, async (value: number) => {
717
- await sliderHandler(SliderActions.sl07, value);
718
- })
719
- watch(() => scanState.value.Slice.TransverseLower, async (value: number) => {
720
- await sliderHandler(SliderActions.sl08, value);
721
- })
722
- watch(() => scanState.value.Orientations.Transverse.Slice, async (value: number) => {
723
- await sliderHandler(SliderActions.sl09, value);
724
- })
725
- watch(() => scanState.value.Slice.TransverseUpper, async (value: number) => {
726
- await sliderHandler(SliderActions.sl10, value);
727
- })
728
- watch(() => scanState.value.Slice.SagittalLower, async (value: number) => {
729
- await sliderHandler(SliderActions.sl11, value);
730
- })
731
- watch(() => scanState.value.Orientations.Sagittal.Slice, async (value: number) => {
732
- await sliderHandler(SliderActions.sl12, value);
733
- })
734
- watch(() => scanState.value.Slice.SagittalUpper, async (value: number) => {
735
- await sliderHandler(SliderActions.sl13, value);
736
- })
737
- watch(() => scanState.value.Slice.CoronalLower, async (value: number) => {
738
- await sliderHandler(SliderActions.sl14, value);
739
- })
740
- watch(() => scanState.value.Orientations.Coronal.Slice, async (value: number) => {
741
- await sliderHandler(SliderActions.sl15, value);
742
- })
743
- watch(() => scanState.value.Slice.CoronalUpper, async (value: number) => {
744
- await sliderHandler(SliderActions.sl16, value);
745
- })
746
- watch(() => scanState.value.InteractionSettings.PanSensivitity, async (value: number) => {
747
- await scanMovementHandler(ScanMovementActions.sm05, value);
748
- })
749
- watch(() => scanState.value.InteractionSettings.ZoomSensitivity, async (value: number) => {
750
- await scanMovementHandler(ScanMovementActions.sm08, value);
751
- })
752
- watch(() => scanState.value.InteractionSettings.RotateSensitivity, async (value: number) => {
753
- await scanMovementHandler(ScanMovementActions.sm10, value);
754
- })
755
- watch(() => scanState.value.InteractionSettings.CameraRotateSensitivity, async (value: number) => {
756
- await scanMovementHandler(ScanMovementActions.sm12, value);
757
- })
758
- watch( () => scanState.value.Layout.PositionData, () => {
759
- componentKey.value += 1
760
- })
903
+ watch(
904
+ () => scanState.value.Display.Brightness,
905
+ async (value: number) => {
906
+ await sliderHandler(SlidersActions.sl01, value);
907
+ }
908
+ );
909
+ watch(
910
+ () => scanState.value.Display.Contrast,
911
+ async (value: number) => {
912
+ await sliderHandler(SlidersActions.sl02, value);
913
+ }
914
+ );
915
+ watch(
916
+ () => scanState.value.Display.Opacity,
917
+ async (value: number) => {
918
+ await sliderHandler(SlidersActions.sl03, value);
919
+ }
920
+ );
921
+ watch(
922
+ () => scanState.value.Display.WindowLower,
923
+ async (value: number) => {
924
+ await sliderHandler(SlidersActions.sl04, value);
925
+ }
926
+ );
927
+ watch(
928
+ () => scanState.value.Display.WindowUpper,
929
+ async (value: number) => {
930
+ await sliderHandler(SlidersActions.sl05, value);
931
+ }
932
+ );
933
+ watch(
934
+ () => scanState.value.Display.ThresholdLower,
935
+ async (value: number) => {
936
+ await sliderHandler(SlidersActions.sl06, value);
937
+ }
938
+ );
939
+ watch(
940
+ () => scanState.value.Display.ThresholdUpper,
941
+ async (value: number) => {
942
+ await sliderHandler(SlidersActions.sl07, value);
943
+ }
944
+ );
945
+ watch(
946
+ () => scanState.value.Slice.TransverseLower,
947
+ async (value: number) => {
948
+ await sliderHandler(SlidersActions.sl08, value);
949
+ }
950
+ );
951
+ watch(
952
+ () => scanState.value.Orientations.Transverse.Slice,
953
+ async (value: number) => {
954
+ await sliderHandler(SlidersActions.sl09, value);
955
+ }
956
+ );
957
+ watch(
958
+ () => scanState.value.Slice.TransverseUpper,
959
+ async (value: number) => {
960
+ await sliderHandler(SlidersActions.sl10, value);
961
+ }
962
+ );
963
+ watch(
964
+ () => scanState.value.Slice.SagittalLower,
965
+ async (value: number) => {
966
+ await sliderHandler(SlidersActions.sl11, value);
967
+ }
968
+ );
969
+ watch(
970
+ () => scanState.value.Orientations.Sagittal.Slice,
971
+ async (value: number) => {
972
+ await sliderHandler(SlidersActions.sl12, value);
973
+ }
974
+ );
975
+ watch(
976
+ () => scanState.value.Slice.SagittalUpper,
977
+ async (value: number) => {
978
+ await sliderHandler(SlidersActions.sl13, value);
979
+ }
980
+ );
981
+ watch(
982
+ () => scanState.value.Slice.CoronalLower,
983
+ async (value: number) => {
984
+ await sliderHandler(SlidersActions.sl14, value);
985
+ }
986
+ );
987
+ watch(
988
+ () => scanState.value.Orientations.Coronal.Slice,
989
+ async (value: number) => {
990
+ await sliderHandler(SlidersActions.sl15, value);
991
+ }
992
+ );
993
+ watch(
994
+ () => scanState.value.Slice.CoronalUpper,
995
+ async (value: number) => {
996
+ await sliderHandler(SlidersActions.sl16, value);
997
+ }
998
+ );
999
+ watch(
1000
+ () => scanState.value.InteractionSettings.PanSensivitity,
1001
+ async (value: number) => {
1002
+ await scanMovementHandler(ScanMovementActions.sm05, value);
1003
+ }
1004
+ );
1005
+ watch(
1006
+ () => scanState.value.InteractionSettings.ZoomSensitivity,
1007
+ async (value: number) => {
1008
+ await scanMovementHandler(ScanMovementActions.sm08, value);
1009
+ }
1010
+ );
1011
+ watch(
1012
+ () => scanState.value.InteractionSettings.RotateSensitivity,
1013
+ async (value: number) => {
1014
+ await scanMovementHandler(ScanMovementActions.sm10, value);
1015
+ }
1016
+ );
1017
+ watch(
1018
+ () => scanState.value.InteractionSettings.CameraRotateSensitivity,
1019
+ async (value: number) => {
1020
+ await scanMovementHandler(ScanMovementActions.sm12, value);
1021
+ }
1022
+ );
761
1023
 
762
1024
  function getIconForPreset(presetName: string): string | undefined {
763
- // if (presetName === 'Bone') return 'fa-solid fa-bone';
764
- // if (presetName === 'Brain') return 'fa-solid fa-brain';
765
- // if (presetName === 'Liver') return '$liver_icon';
766
- // if (presetName === 'Lungs') return 'fa-solid fa-lungs';
767
- // if (presetName === 'Muscle') return '$muscle_icon';
768
- // if (presetName === 'Temporal Bones') return '$temporal_bones_icon';
769
- // if (presetName === 'Soft Tissue') return '$torso_icon';
770
- // if (presetName === 'Skin') return '$skin_icon';
771
- return undefined
1025
+ if (presetName === "Bone") return "fa:fas fa-bone";
1026
+ if (presetName === "Brain") return "fa:fas fa-brain";
1027
+ if (presetName === "Liver") return "custom:liver_icon";
1028
+ if (presetName === "Lungs") return "fa:fas fa-lungs";
1029
+ if (presetName === "Muscle") return "custom:muscle_icon";
1030
+ if (presetName === "Temporal Bones") return "custom:temporal_bones_icon";
1031
+ if (presetName === "Soft Tissue") return "custom:torso_icon";
1032
+ if (presetName === "Skin") return "custom:skin_icon";
1033
+ return undefined;
772
1034
  }
773
1035
  async function rotateByDeg(view: ScanView, deg: number) {
774
- await (FrontEndInterfaces.scan_orientation, ScanOrientationActions.so01, {
775
- Version: '0.0.1',
776
- View: view,
777
- Angle: deg,
778
- });
1036
+ await sendPayload(
1037
+ FrontEndInterfaces.scan_orientation,
1038
+ ScanOrientationActions.so01,
1039
+ {
1040
+ Version: "0.0.1",
1041
+ View: view,
1042
+ Angle: deg,
1043
+ }
1044
+ );
779
1045
  }
780
1046
  function getCurrentActiveView(position: PositionData): ScanView {
781
- return unref(scanState).Layout.PositionData.length !== 1 ? position.DefaultView : unref(scanState).CurrentView;
1047
+ return unref(scanState).Layout.PositionData.length !== 1
1048
+ ? position.DefaultView
1049
+ : unref(scanState).CurrentView;
782
1050
  }
783
1051
  function getViewName(index: number) {
784
1052
  return Object.values(ScanView)
785
- .filter((value) => typeof value === 'string')
1053
+ .filter((value) => typeof value === "string")
786
1054
  .map((x) => {
787
- if (x === 'Volume') return '3D Volume';
1055
+ if (x === "Volume") return "3D Volume";
788
1056
  return x;
789
1057
  })[index];
790
1058
  }
791
1059
  async function fullscreenLayout(view: ScanView) {
792
- await layouts('lo_01');
1060
+ await layouts("lo_01");
793
1061
  await viewSelection(`vs_0${view + 1}`);
794
1062
  }
795
- // generateDivStyleForLayout,
796
- async function scanMovementHandler(action: string, value: number) {
1063
+ async function scanMovementHandler(action: string, value: number) {
797
1064
  if (unref(transactionStarted)) return;
798
1065
  await scanMovement(action, value);
799
1066
  }
@@ -805,55 +1072,77 @@ async function sliderHandler(action: string, value: number) {
805
1072
  function closeModal() {
806
1073
  m_closeDialog.value = false;
807
1074
  value.value = false;
1075
+ executeOption("OnExitViewer");
808
1076
  }
809
1077
  function alterValue(val: boolean) {
810
1078
  if (!val) {
811
1079
  m_closeDialog.value = true;
812
- return
1080
+ return;
1081
+ } else {
1082
+ isDemo.value =
1083
+ props.payload.Url ===
1084
+ "https://webgl-3dr.singular.health/test_scans/01440d4e-8b04-4b90-bb2c-698535ce16d6/CHEST.3vxl";
813
1085
  }
814
1086
 
815
1087
  value.value = val;
816
1088
  }
817
- async function sendPayload(interfaceType: string, actionType: string, message: any) {
1089
+ async function sendPayload(
1090
+ interfaceType: string,
1091
+ actionType: string,
1092
+ message: any
1093
+ ) {
818
1094
  await unref(web_gl)?.sendPayload(
819
1095
  JSON.stringify({
820
- Version: '0.0.1',
1096
+ Version: "0.0.1",
821
1097
  Interface: interfaceType,
822
1098
  Action: actionType,
823
1099
  Message: JSON.stringify(message),
824
- }),
1100
+ })
825
1101
  );
826
1102
  }
827
1103
  async function load() {
828
1104
  instanceLoaded.value = true;
829
1105
  scanLoading.value = true;
830
- await sendPayload('file_management', 'fm_01', props.payload);
1106
+ await sendPayload("file_management", "fm_01", props.payload);
831
1107
  }
832
1108
  async function viewSelection(action: string) {
833
- await sendPayload(FrontEndInterfaces.view_selection, action, emptyPayload)
1109
+ await sendPayload(FrontEndInterfaces.view_selection, action, emptyPayload);
834
1110
  }
835
1111
  async function layouts(action: string) {
836
- if (action !== 'lo_01') previousLayout.value = action;
837
- await sendPayload(FrontEndInterfaces.layout, action, emptyPayload)
1112
+ if (action !== "lo_01") previousLayout.value = action;
1113
+ await sendPayload(FrontEndInterfaces.layout, action, emptyPayload);
838
1114
  }
839
1115
  async function snap() {
840
- // await unref(web_gl).snap();
1116
+ emit("snap");
841
1117
  }
842
- async function setPreset(action: PresetActions, preset: any) {
843
- await sendPayload('presets', action, preset)
844
- if (action === PresetActions.pr02) {
1118
+
1119
+ async function setPreset(action: PresetsActions, preset: any) {
1120
+ await sendPayload("presets", action, preset);
1121
+ if (action === PresetsActions.pr02) {
845
1122
  currentColourPreset.value = preset;
846
1123
  }
847
1124
  }
848
1125
  async function slider(action: string, value: number) {
849
- await sendPayload('sliders', action, { Version: '0.0.1', Value: toNumber(value) })
1126
+ await sendPayload("sliders", action, {
1127
+ Version: "0.0.1",
1128
+ Value: toNumber(value),
1129
+ });
850
1130
  }
851
1131
  async function scanMovement(action: string, value: number) {
852
- await sendPayload('scan_movement', action, { Version: '0.0.1', Value: toNumber(value) })
1132
+ await sendPayload(FrontEndInterfaces.scan_movement, action, {
1133
+ Version: "0.0.1",
1134
+ Value: toNumber(value),
1135
+ });
853
1136
  }
854
1137
  async function hoverOverCanvas(isHovering: boolean) {
855
- await sendPayload('interactivity', 'in_01', { Version: '0.0.1', Value: isHovering })
856
- await sendPayload('interactivity', 'in_02', { Version: '0.0.1', Value: isHovering })
1138
+ await sendPayload("interactivity", "in_01", {
1139
+ Version: "0.0.1",
1140
+ Value: isHovering,
1141
+ });
1142
+ await sendPayload("interactivity", "in_02", {
1143
+ Version: "0.0.1",
1144
+ Value: isHovering,
1145
+ });
857
1146
  }
858
1147
  async function i_scanState(_: string, message: string) {
859
1148
  transactionStarted.value = true;
@@ -885,32 +1174,61 @@ async function i_fileManagement(action: string, message: string) {
885
1174
  drawerCollapsed.value = false;
886
1175
  await hoverOverCanvas(false);
887
1176
  }
1177
+
1178
+ import { useNotification } from "@kyvg/vue3-notification";
1179
+
1180
+ const { notify } = useNotification();
1181
+
888
1182
  async function i_notifications(action: string, message: string) {
889
- // const obj = JSON.parse(message);
890
- // console.log(obj);
891
- //{\"Version\":\"1.0.0\",\"Interface\":\"interactivity\",\"Action\":\"in_01\",\"Code\":\"0000\",\"Description\":\"\"}"
892
-
893
- // if (obj.Description) {
894
- // if (action === NotificationActions.no01) {
895
- // await NotificationService.Instantiate().notificationSuccess(obj.Description);
896
- // }
897
- // if (action === NotificationActions.no02) {
898
- // await NotificationService.Instantiate().notificationError(obj.Description);
899
- // }
900
- // if (action === NotificationActions.no03) {
901
- // await NotificationService.Instantiate().notificationWarning(obj.Description);
902
- // }
903
- // }
1183
+ const notification = JSON.parse(message) as NotificationPayload;
1184
+
1185
+ switch (notification.Action) {
1186
+ case InteractivityActions.in01:
1187
+ case InteractivityActions.in02:
1188
+ case InteractivityActions.in03:
1189
+ case InteractivityActions.in04:
1190
+ return;
1191
+ default:
1192
+ }
1193
+ if (notification.Action.startsWith("sl")) return;
1194
+
1195
+ let type = "success";
1196
+ switch (action) {
1197
+ // Muting no01
1198
+ case NotificationsActions.no01:
1199
+ type = "success";
1200
+ return;
1201
+
1202
+ case NotificationsActions.no02:
1203
+ type = "warn";
1204
+ break;
1205
+
1206
+ case NotificationsActions.no03:
1207
+ type = "error";
1208
+ break;
1209
+
1210
+ // Muting no04
1211
+ case NotificationsActions.no04:
1212
+ type = "info";
1213
+ return;
1214
+ }
1215
+
1216
+ notify({
1217
+ title: notification.Code,
1218
+ text: notification.Action,
1219
+ type,
1220
+ });
904
1221
  }
905
1222
  const isLayout2x2 = computed(() => {
906
1223
  return (
907
1224
  unref(scanState).Layout.PositionData.length > 1 &&
908
1225
  unref(scanState).Layout.PositionData[0].Anchor === AnchorPoint.TOP_LEFT &&
909
1226
  unref(scanState).Layout.PositionData[1].Anchor === AnchorPoint.TOP_RIGHT &&
910
- unref(scanState).Layout.PositionData[2].Anchor === AnchorPoint.BOTTOM_LEFT &&
1227
+ unref(scanState).Layout.PositionData[2].Anchor ===
1228
+ AnchorPoint.BOTTOM_LEFT &&
911
1229
  unref(scanState).Layout.PositionData[3].Anchor === AnchorPoint.BOTTOM_RIGHT
912
- )
913
- })
1230
+ );
1231
+ });
914
1232
  const isLayout1x3 = computed(() => {
915
1233
  return (
916
1234
  unref(scanState).Layout.PositionData.length > 1 &&
@@ -918,19 +1236,20 @@ const isLayout1x3 = computed(() => {
918
1236
  unref(scanState).Layout.PositionData[1].Anchor === AnchorPoint.TOP_RIGHT &&
919
1237
  unref(scanState).Layout.PositionData[2].Anchor === AnchorPoint.RIGHT &&
920
1238
  unref(scanState).Layout.PositionData[3].Anchor === AnchorPoint.BOTTOM_RIGHT
921
- )
922
- })
923
-
1239
+ );
1240
+ });
924
1241
 
925
1242
  function getCurrentGreyscalePreset() {
926
1243
  for (const preset of unref(initialScanState).GreyscalePresets) {
927
1244
  if (
928
1245
  (unref(scanState).Display.WindowLower === preset.Lower ||
929
- (preset.Lower > unref(initialScanState).HuLower &&
930
- unref(scanState).Display.WindowLower === unref(initialScanState).HuLower)) &&
1246
+ (preset.Lower < unref(initialScanState).HuLower &&
1247
+ unref(scanState).Display.WindowLower ===
1248
+ unref(initialScanState).HuLower)) &&
931
1249
  (unref(scanState).Display.WindowUpper === preset.Upper ||
932
1250
  (preset.Upper > unref(initialScanState).HuUpper &&
933
- unref(scanState).Display.WindowUpper === unref(initialScanState).HuUpper))
1251
+ unref(scanState).Display.WindowUpper ===
1252
+ unref(initialScanState).HuUpper))
934
1253
  ) {
935
1254
  return preset.Name;
936
1255
  }
@@ -941,18 +1260,52 @@ function setScanState(message: string) {
941
1260
  scanState.value = JSON.parse(message) as CurrentScanState;
942
1261
  }
943
1262
 
944
- function handleOnPayload(interfaceSet: string | FrontEndInterfaces, actionSet: string, message: string) {
945
- if (interfaceSet === FrontEndInterfaces.scan_loading || interfaceSet === 'scan_state') {
946
- i_scanState(actionSet, message)
1263
+ function handleOnPayload(
1264
+ interfaceSet: string | FrontEndInterfaces,
1265
+ actionSet: string,
1266
+ message: string
1267
+ ) {
1268
+ if (interfaceSet === FrontEndInterfaces.scan_state) {
1269
+ i_scanState(actionSet, message);
947
1270
  }
948
1271
  if (interfaceSet === FrontEndInterfaces.file_management) {
949
- i_fileManagement(actionSet, message)
1272
+ i_fileManagement(actionSet, message);
950
1273
  }
951
- if (interfaceSet === 'notifications') {
952
- i_notifications(actionSet, message)
1274
+ if (interfaceSet === FrontEndInterfaces.notifications) {
1275
+ i_notifications(actionSet, message);
953
1276
  }
954
1277
  }
955
1278
 
1279
+ defineExpose({
1280
+ alterValue,
1281
+ load,
1282
+ handleOnPayload,
1283
+ setScanState,
1284
+ sendPayload,
1285
+ getIconForPreset,
1286
+ cSlider,
1287
+ sSlider,
1288
+ tSlider,
1289
+ thresholdSlider,
1290
+ windowSlider,
1291
+ isLayout2x2,
1292
+ isLayout1x3,
1293
+ m_closeDialog,
1294
+ getCurrentGreyscalePreset,
1295
+ closeModal,
1296
+ snap,
1297
+ scanState,
1298
+ initialScanState,
1299
+ footerItems,
1300
+ rotateByDeg,
1301
+ getCurrentActiveView,
1302
+ getViewName,
1303
+ fullscreenLayout,
1304
+ layouts,
1305
+ setPreset,
1306
+ value,
1307
+ transactionStarted,
1308
+ });
956
1309
  </script>
957
1310
  <style>
958
1311
  .v-dialog:not(.v-dialog--fullscreen) {