@3cr/viewer-browser 0.0.162 → 0.0.194

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 (188) hide show
  1. package/.circleci/config.yml +8 -6
  2. package/__tests__/index.spec.ts +31 -24
  3. package/components.d.ts +32 -0
  4. package/dist/Viewer3CR.js +44 -27
  5. package/dist/Viewer3CR.mjs +22731 -17742
  6. package/dist/Viewer3CR.umd.js +44 -27
  7. package/index.html +5 -8
  8. package/index.ts +7 -2
  9. package/package.json +4 -2
  10. package/playground/index.html +6 -4
  11. package/src/App.vue +12 -4
  12. package/src/__tests__/app.spec.ts +2 -13
  13. package/src/assets/magic_wand.svg +24 -0
  14. package/src/components/WebGL3DR.vue +53 -92
  15. package/src/components/__tests__/webgl3dr.spec.ts +29 -48
  16. package/src/{demo → components/demo}/DemoModal.vue +1 -1
  17. package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
  18. package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
  19. package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
  20. package/src/components/demo/__tests__/options.spec.ts +25 -0
  21. package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
  22. package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
  23. package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
  24. package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
  25. package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
  26. package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
  27. package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
  28. package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
  29. package/src/{demo → components/demo}/options.ts +18 -20
  30. package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
  31. package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
  32. package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
  33. package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
  34. package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
  35. package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
  36. package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
  37. package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
  38. package/src/components/loading/LoadingSpinner.vue +1 -1
  39. package/src/components/modal/ActionRail.vue +96 -0
  40. package/src/components/modal/AskAI.vue +250 -0
  41. package/src/components/modal/CloseViewerModal.vue +104 -0
  42. package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
  43. package/src/components/modal/ViewerActionRail.vue +123 -0
  44. package/src/components/modal/ViewerAnnotationModal.vue +115 -0
  45. package/src/components/modal/ViewerAnnotations.vue +283 -0
  46. package/src/components/modal/ViewerDisplaySettings.vue +102 -0
  47. package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
  48. package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
  49. package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
  50. package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
  51. package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
  52. package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
  53. package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
  54. package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
  55. package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
  56. package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
  57. package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
  58. package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
  59. package/src/components/modal/actions/Action.vue +40 -0
  60. package/src/components/modal/actions/Flip3dAction.vue +79 -0
  61. package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
  62. package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
  63. package/src/components/modal/actions/FullscreenAction.vue +47 -0
  64. package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
  65. package/src/components/modal/actions/PanAction.vue +78 -0
  66. package/src/components/modal/actions/ResetViewAction.vue +29 -0
  67. package/src/components/modal/actions/Rotate2dAction.vue +78 -0
  68. package/src/components/modal/actions/Slice3dAction.vue +71 -0
  69. package/src/components/modal/actions/ZoomAction.vue +70 -0
  70. package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
  71. package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
  72. package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
  73. package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
  74. package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
  75. package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
  76. package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
  77. package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
  78. package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
  79. package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
  80. package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
  81. package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
  82. package/src/components/modal/composables/useEventListener.ts +22 -0
  83. package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
  84. package/src/components/selectors/ValueSelector.vue +30 -33
  85. package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
  86. package/src/components/sliders/DoubleSliderSelector.vue +79 -71
  87. package/src/components/sliders/VerticalSliderSelector.vue +12 -17
  88. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
  89. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
  90. package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
  91. package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
  92. package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
  93. package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
  94. package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
  95. package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
  96. package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
  97. package/src/dataLayer/clamp.ts +9 -0
  98. package/src/dataLayer/eventHandlers.ts +26 -0
  99. package/src/dataLayer/patchDataOverlay.ts +101 -0
  100. package/src/dataLayer/scanState.ts +105 -26
  101. package/src/dataLayer/useViewer3cr.ts +7 -0
  102. package/src/dataLayer/viewer3cr.ts +410 -0
  103. package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
  104. package/src/helpers/__tests__/model-helper.spec.ts +44 -13
  105. package/src/helpers/layoutOverlayStyle.ts +16 -27
  106. package/src/helpers/modelHelper.ts +62 -10
  107. package/src/models/Callbacks.ts +2 -2
  108. package/src/models/LoadViewerOptions.ts +2 -0
  109. package/src/models/LoadViewerPayload.ts +1 -0
  110. package/src/notifications/notification.ts +3 -4
  111. package/src/plugins/vuetify.ts +5 -0
  112. package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
  113. package/src/services/gpt/gpt.service.ts +27 -0
  114. package/static/3cr-types-browser/index.ts +74 -0
  115. package/static/3cr-types-browser/types/Action.ts +6 -0
  116. package/static/3cr-types-browser/types/ActionData.ts +4 -0
  117. package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
  118. package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
  119. package/static/3cr-types-browser/types/CallToAction.ts +5 -0
  120. package/static/3cr-types-browser/types/ColourData.ts +7 -0
  121. package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
  122. package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
  123. package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
  124. package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
  125. package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
  126. package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
  127. package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
  128. package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
  129. package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
  130. package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
  131. package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
  132. package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
  133. package/static/3cr-types-browser/types/FlipValue.ts +7 -0
  134. package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
  135. package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
  136. package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
  137. package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
  138. package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
  139. package/static/3cr-types-browser/types/InteractionType.ts +8 -0
  140. package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
  141. package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
  142. package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
  143. package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
  144. package/static/3cr-types-browser/types/LayoutData.ts +7 -0
  145. package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
  146. package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
  147. package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
  148. package/static/3cr-types-browser/types/MovementData.ts +7 -0
  149. package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
  150. package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
  151. package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
  152. package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
  153. package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
  154. package/static/3cr-types-browser/types/Object.ts +1 -0
  155. package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
  156. package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
  157. package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
  158. package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
  159. package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
  160. package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
  161. package/static/3cr-types-browser/types/PositionData.ts +14 -0
  162. package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
  163. package/static/3cr-types-browser/types/RotationValue.ts +7 -0
  164. package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
  165. package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
  166. package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
  167. package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
  168. package/static/3cr-types-browser/types/ScanView.ts +6 -0
  169. package/static/3cr-types-browser/types/SettingsData.ts +12 -0
  170. package/static/3cr-types-browser/types/SlicerData.ts +9 -0
  171. package/static/3cr-types-browser/types/SliderValue.ts +4 -0
  172. package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
  173. package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
  174. package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
  175. package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
  176. package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
  177. package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
  178. package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
  179. package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
  180. package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
  181. package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
  182. package/test/helper.ts +10 -1
  183. package/test/setup.ts +13 -0
  184. package/tsconfig.json +1 -0
  185. package/vite.config.mts +1 -0
  186. package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
  187. package/src/dataLayer/payloadHandler.ts +0 -138
  188. /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
@@ -0,0 +1,331 @@
1
+ import {beforeEach, describe, expect, it, MockInstance, vi} from "vitest";
2
+ import {emptyPayload, Viewer3cr} from "@/dataLayer/viewer3cr";
3
+ import {unref} from "vue";
4
+ import {
5
+ AnchorPoint,
6
+ DataOverlayActions,
7
+ FrontEndInterfaces, InteractivityActions,
8
+ LayoutActions, NavigationCubeActions,
9
+ PresetsActions,
10
+ ScanMovementActions,
11
+ ScanOrientationActions,
12
+ ScanView,
13
+ SlidersActions,
14
+ } from "@3cr/types-ts";
15
+ import {toNumber} from "@/helpers/utils";
16
+ import {currentColourPreset, previousLayout, transactionStarted,} from "@/dataLayer/scanState";
17
+ import {useViewer3cr} from "@/dataLayer/useViewer3cr";
18
+
19
+ describe('Viewer3cr tests', () => {
20
+ const viewer3cr = useViewer3cr();
21
+
22
+ let sendPayload: MockInstance;
23
+
24
+ beforeEach(() => {
25
+ sendPayload = vi.spyOn(Viewer3cr.prototype, 'sendPayload').mockResolvedValue();
26
+ });
27
+
28
+ it('should create instance', () => {
29
+ expect(viewer3cr).toBeTruthy();
30
+ });
31
+
32
+ describe('data overlay actions', () => {
33
+ it('should toggle 2d annotation', async () => {
34
+ await viewer3cr.toggle2dAnnotation('test', true);
35
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.data_overlay, DataOverlayActions.do03, {
36
+ Version: expect.any(String),
37
+ Id: 'test',
38
+ Visibility: true
39
+ });
40
+ });
41
+
42
+ it('should toggle 3d annotation', async () => {
43
+ await viewer3cr.toggle3dAnnotation('test', true);
44
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.data_overlay, DataOverlayActions.do11, {
45
+ Version: expect.any(String),
46
+ Id: 'test',
47
+ Visibility: true
48
+ });
49
+ });
50
+ });
51
+
52
+ describe('interactivity actions', () => {
53
+ it('should enable/disable unity inputs', async () => {
54
+ await viewer3cr.hoverOverCanvas(true);
55
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.interactivity, InteractivityActions.in01, {
56
+ Version: expect.any(String),
57
+ Value: true
58
+ });
59
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.interactivity, InteractivityActions.in02, {
60
+ Version: expect.any(String),
61
+ Value: true
62
+ });
63
+ });
64
+ });
65
+
66
+ describe('layout actions', () => {
67
+ beforeEach(() => {
68
+ previousLayout.value = LayoutActions.lo01;
69
+ });
70
+
71
+ it('should layouts', async () => {
72
+ const action = LayoutActions.lo01;
73
+ expect(sendPayload).not.toHaveBeenCalled();
74
+ await viewer3cr.layouts(action);
75
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.layout, action, emptyPayload);
76
+ });
77
+
78
+ it('should set prevLayout', async () => {
79
+ const action = LayoutActions.lo02;
80
+ expect(sendPayload).not.toHaveBeenCalled();
81
+ expect(unref(previousLayout)).not.toBe(LayoutActions.lo02);
82
+ await viewer3cr.layouts(action);
83
+ expect(unref(previousLayout)).toBe(LayoutActions.lo02);
84
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.layout, action, emptyPayload);
85
+ });
86
+ });
87
+
88
+ describe('navigation actions', () => {
89
+ it('should set nav cube position', async () => {
90
+ await viewer3cr.setNavCubePositionSize(AnchorPoint.TOP_LEFT, 10, 10, 100, 100);
91
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc01, {
92
+ Version: expect.any(String),
93
+ AnchorPoint: AnchorPoint.TOP_LEFT,
94
+ Position: { Version: expect.any(String), X: 10, Y: 10 },
95
+ Size: { Version: expect.any(String), X: 100, Y: 100 }
96
+ });
97
+ });
98
+
99
+ it('should set nav cube visibility', async () => {
100
+ await viewer3cr.setNavCubeVisibility(true);
101
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc02, {
102
+ Version: expect.any(String),
103
+ Value: true
104
+ });
105
+ });
106
+
107
+ it('should set nav cube interactivity', async () => {
108
+ await viewer3cr.setNavCubeInteractivity(true);
109
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc03, {
110
+ Version: expect.any(String),
111
+ Value: true
112
+ });
113
+ });
114
+
115
+ it('should set nav cube perspective', async () => {
116
+ await viewer3cr.setNavCubePerspective(true);
117
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc04, {
118
+ Version: expect.any(String),
119
+ Value: true
120
+ });
121
+ });
122
+
123
+ it('should set nav cube colour/opacity', async () => {
124
+ const colour = { R: 100, G: 200, B: 50, A: 180 };
125
+ await viewer3cr.setNavCubeColourOpacity(colour.R, colour.G, colour.B, colour.A);
126
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc05, {
127
+ Version: expect.any(String),
128
+ R: colour.R / 255,
129
+ G: colour.G / 255,
130
+ B: colour.B / 255,
131
+ A: colour.A / 255
132
+ });
133
+ });
134
+
135
+ it('should set nav cube highlight colour/opacity', async () => {
136
+ const colour = { R: 100, G: 200, B: 50, A: 180 };
137
+ await viewer3cr.setNavCubeHighlightColourOpacity(colour.R, colour.G, colour.B, colour.A);
138
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.navigation_cube, NavigationCubeActions.nc07, {
139
+ Version: expect.any(String),
140
+ R: colour.R / 255,
141
+ G: colour.G / 255,
142
+ B: colour.B / 255,
143
+ A: colour.A / 255
144
+ });
145
+ });
146
+ });
147
+
148
+ describe('preset actions', () => {
149
+ it("should set setPreset", async () => {
150
+ const action = PresetsActions.pr01;
151
+ const data = { asd: "123" };
152
+ expect(sendPayload).not.toHaveBeenCalled();
153
+ expect(unref(currentColourPreset)).not.toBe(data);
154
+ await viewer3cr.setPreset(action, data);
155
+ expect(unref(currentColourPreset)).not.toBe(data);
156
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.presets, action, data);
157
+ });
158
+
159
+ it("should set setPreset change colour", async () => {
160
+ const action = PresetsActions.pr02;
161
+ const data = { asd: "123", };
162
+ expect(sendPayload).not.toHaveBeenCalled();
163
+ expect(unref(currentColourPreset)).not.toBe(data);
164
+ await viewer3cr.setPreset(action, data);
165
+ expect(unref(currentColourPreset)).toStrictEqual(data);
166
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.presets, action, data);
167
+ });
168
+ });
169
+
170
+ describe('scan movement actions', () => {
171
+ it("should scanMovementHandler", async () => {
172
+ const action = ScanMovementActions.sm05;
173
+ const value = 100;
174
+ expect(sendPayload).not.toHaveBeenCalled();
175
+ await viewer3cr.scanMovementHandler(action, value);
176
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, action, {
177
+ Version: expect.any(String),
178
+ Value: toNumber(value),
179
+ });
180
+ });
181
+
182
+ it("should not scanMovementHandler transactionStarted", async () => {
183
+ transactionStarted.value = true;
184
+ const action = ScanMovementActions.sm05;
185
+ const value = 100;
186
+ expect(sendPayload).not.toHaveBeenCalled();
187
+ await viewer3cr.scanMovementHandler(action, value);
188
+ expect(sendPayload).not.toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, action, {
189
+ Version: expect.any(String),
190
+ Value: toNumber(value),
191
+ });
192
+ });
193
+
194
+ it('should pan left', async () => {
195
+ const view = ScanView.Volume;
196
+ const movement = 10;
197
+ await viewer3cr.panLeft(view, movement);
198
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm13, {
199
+ Version: expect.any(String),
200
+ View: view,
201
+ Movement: movement,
202
+ });
203
+ });
204
+
205
+ it('should pan right', async () => {
206
+ const view = ScanView.Volume;
207
+ const movement = 10;
208
+ await viewer3cr.panRight(view, movement);
209
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm14, {
210
+ Version: expect.any(String),
211
+ View: view,
212
+ Movement: movement,
213
+ });
214
+ });
215
+
216
+ it('should pan up', async () => {
217
+ const view = ScanView.Volume;
218
+ const movement = 10;
219
+ await viewer3cr.panUp(view, movement);
220
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm15, {
221
+ Version: expect.any(String),
222
+ View: view,
223
+ Movement: movement,
224
+ });
225
+ });
226
+
227
+ it('should pan down', async () => {
228
+ const view = ScanView.Volume;
229
+ const movement = 10;
230
+ await viewer3cr.panDown(view, movement);
231
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm16, {
232
+ Version: expect.any(String),
233
+ View: view,
234
+ Movement: movement,
235
+ });
236
+ });
237
+
238
+ it('should zoom in', async () => {
239
+ const view = ScanView.Volume;
240
+ const movement = 10;
241
+ await viewer3cr.zoomIn(view, movement);
242
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm17, {
243
+ Version: expect.any(String),
244
+ View: view,
245
+ Movement: movement,
246
+ });
247
+ });
248
+
249
+ it('should zoom out', async () => {
250
+ const view = ScanView.Volume;
251
+ const movement = 10;
252
+ await viewer3cr.zoomOut(view, movement);
253
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_movement, ScanMovementActions.sm18, {
254
+ Version: expect.any(String),
255
+ View: view,
256
+ Movement: movement,
257
+ });
258
+ });
259
+ });
260
+
261
+ describe('scan orientation actions', () => {
262
+ it("should rotateByDeg", async () => {
263
+ const view = ScanView.Coronal;
264
+ const deg = 100;
265
+ expect(sendPayload).not.toHaveBeenCalled();
266
+ await viewer3cr.rotateByDeg(view, deg);
267
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_orientation, ScanOrientationActions.so01, {
268
+ Version: expect.any(String),
269
+ View: view,
270
+ Angle: deg,
271
+ });
272
+ });
273
+
274
+ it('should flip 2d view horizontally', async () => {
275
+ await viewer3cr.flipHorizontally(ScanView.Sagittal, true);
276
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_orientation, ScanOrientationActions.so02, {
277
+ Version: expect.any(String),
278
+ View: ScanView.Sagittal,
279
+ Flipped: true
280
+ });
281
+ });
282
+
283
+ it('should flip 2d view vertically', async () => {
284
+ await viewer3cr.flipVertically(ScanView.Sagittal, true);
285
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_orientation, ScanOrientationActions.so03, {
286
+ Version: expect.any(String),
287
+ View: ScanView.Sagittal,
288
+ Flipped: true
289
+ });
290
+ });
291
+
292
+ it('should invert transform', async () => {
293
+ await viewer3cr.invertTransform(true, true, true);
294
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.scan_orientation, ScanOrientationActions.so04, {
295
+ Version: expect.any(String),
296
+ InvertedSagittal: true,
297
+ InvertedCoronal: true,
298
+ InvertedTransverse: true
299
+ });
300
+ });
301
+ });
302
+
303
+ describe('slider actions', () => {
304
+ beforeEach(() => {
305
+ transactionStarted.value = false;
306
+ });
307
+
308
+ it("should sliderHandler", async () => {
309
+ const action = SlidersActions.sl08;
310
+ const value = 100;
311
+ expect(sendPayload).not.toHaveBeenCalled();
312
+ await viewer3cr.sliderHandler(action, value);
313
+ expect(sendPayload).toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, {
314
+ Version: expect.any(String),
315
+ Value: toNumber(value),
316
+ });
317
+ });
318
+
319
+ it("should not sliderHandler transactionStarted", async () => {
320
+ transactionStarted.value = true;
321
+ const action = SlidersActions.sl08;
322
+ const value = 100;
323
+ expect(sendPayload).not.toHaveBeenCalled();
324
+ await viewer3cr.sliderHandler(action, value);
325
+ expect(sendPayload).not.toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, {
326
+ Version: expect.any(String),
327
+ Value: toNumber(value),
328
+ });
329
+ });
330
+ });
331
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Clamps a number to min if value < min, or max if value > max, otherwise return value.
3
+ * @param value The value to clamp
4
+ * @param min The minimum value.
5
+ * @param max The maximum value.
6
+ */
7
+ export function clamp(value: number, min: number, max: number): number {
8
+ return Math.min(Math.max(value, min), max);
9
+ }
@@ -0,0 +1,26 @@
1
+ export class EventHandlers<Key, Callback extends CallableFunction> {
2
+ private callbacks = new Map<Key, Set<Callback>>();
3
+
4
+ add(key: Key, callback: Callback): void {
5
+ if (this.callbacks.has(key)) {
6
+ this.callbacks.get(key)!.add(callback);
7
+ } else {
8
+ this.callbacks.set(key, new Set([callback]));
9
+ }
10
+ }
11
+
12
+ remove(key: Key, callback: Callback): boolean {
13
+ if (this.callbacks.has(key)) {
14
+ return this.callbacks.get(key)!.delete(callback);
15
+ }
16
+ return false;
17
+ }
18
+
19
+ async call(key: Key, ...args: unknown[]): Promise<void> {
20
+ if (this.callbacks.has(key)) {
21
+ for (const callback of this.callbacks.get(key)!) {
22
+ await callback(...args);
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,101 @@
1
+ import {
2
+ Action,
3
+ ActionData,
4
+ CallToAction,
5
+ CurrentDataOverlayState,
6
+ DataOverlay,
7
+ DataOverlayData, DataOverlayEvent,
8
+ InitialDataOverlayState
9
+ } from "@3cr/types-ts";
10
+
11
+ export type DataOverlayDataUnpatched = Omit<DataOverlayData, 'Data'> & {
12
+ Data: string;
13
+ };
14
+
15
+ export type DataOverlayUnpatched = Omit<DataOverlay, 'CallToAction'> & {
16
+ CallToAction: string | null;
17
+ };
18
+
19
+ export type CallToActionUnpatched = Omit<CallToAction, 'Actions'> & {
20
+ Actions: ActionUnpatched[];
21
+ };
22
+
23
+ export type ActionUnpatched = Omit<Action, 'ActionData'> & {
24
+ ActionData: string;
25
+ };
26
+
27
+ export type CurrentDataOverlayStateUnpatched = Omit<CurrentDataOverlayState, 'DataOverlay'> & {
28
+ DataOverlay: DataOverlayDataUnpatched[];
29
+ };
30
+
31
+ export type InitialDataOverlayStateUnpatched = Omit<InitialDataOverlayState, 'DataOverlay'> & {
32
+ DataOverlay: CurrentDataOverlayStateUnpatched;
33
+ };
34
+
35
+ export type DataOverlayEventUnpatched = Omit<DataOverlayEvent, 'Annotation'> & {
36
+ Annotation: DataOverlayUnpatched;
37
+ }
38
+
39
+ export function patchAction(data: ActionUnpatched) {
40
+ return {
41
+ ...data,
42
+ ActionData: JSON.parse(data.ActionData) as ActionData
43
+ };
44
+ }
45
+
46
+ export function patchDataOverlay(data: DataOverlayUnpatched) {
47
+ return {
48
+ ...data,
49
+ CallToAction: data.CallToAction ? JSON.parse(data.CallToAction) as CallToActionUnpatched : null
50
+ };
51
+ }
52
+
53
+ export function patchDataOverlayData(data: DataOverlayDataUnpatched) {
54
+ return {
55
+ ...data,
56
+ Data: JSON.parse(data.Data) as DataOverlayUnpatched
57
+ }
58
+ }
59
+
60
+ export function patchInitialDataOverlay(data: InitialDataOverlayStateUnpatched): InitialDataOverlayState {
61
+ return {
62
+ ...data,
63
+ DataOverlay: patchCurrentDataOverlay(data.DataOverlay)
64
+ };
65
+ }
66
+
67
+ export function patchCurrentDataOverlay(data: CurrentDataOverlayStateUnpatched): CurrentDataOverlayState {
68
+ return {
69
+ ...data,
70
+ DataOverlay: data.DataOverlay.map(overlay => {
71
+ const patchedDataOverlayData = patchDataOverlayData(overlay);
72
+ const patchedDataOverlay = patchDataOverlay(patchedDataOverlayData.Data);
73
+ return {
74
+ ...patchedDataOverlayData,
75
+ Data: {
76
+ ...patchedDataOverlay,
77
+ CallToAction: patchedDataOverlay.CallToAction ? {
78
+ Actions: patchedDataOverlay.CallToAction.Actions.map(action => {
79
+ return patchAction(action);
80
+ })
81
+ } : null
82
+ }
83
+ };
84
+ })
85
+ };
86
+ }
87
+
88
+ export function patchDataOverlayEvent(data: DataOverlayEventUnpatched): DataOverlayEvent {
89
+ const patchedDataOverlay = patchDataOverlay(data.Annotation);
90
+ return {
91
+ ...data,
92
+ Annotation: {
93
+ ...patchedDataOverlay,
94
+ CallToAction: patchedDataOverlay.CallToAction ? {
95
+ Actions: patchedDataOverlay.CallToAction.Actions.map(action => {
96
+ return patchAction(action);
97
+ })
98
+ } : null
99
+ }
100
+ };
101
+ }