@ohif/app 3.0.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/151.bundle.07bac9172580a60fae7a.js +2579 -0
  2. package/dist/192.bundle.62be5f0ef9705a485071.js +894 -0
  3. package/dist/199.bundle.2286f24cf0a068e7f50c.js +480 -0
  4. package/dist/205.bundle.39e6c847d618ad2b1b7a.js +62 -0
  5. package/dist/208.bundle.23748a85dfdc79c05d3a.js +864 -0
  6. package/dist/270.bundle.abbdb5348274bae3e8bc.js +23906 -0
  7. package/dist/283.bundle.33f99a75a5e2d9333da2.js +2939 -0
  8. package/dist/295.bundle.5105ce962be15c92484d.js +48 -0
  9. package/dist/331.bundle.7ac7b142d249d14fd99e.js +73034 -0
  10. package/dist/351.bundle.c5d7279ef42e30f61e08.js +1471 -0
  11. package/dist/351.css +3 -0
  12. package/dist/36785fbd89b0e17f6099.wasm +0 -0
  13. package/dist/381.bundle.0905e683605fcbc0895f.js +1009 -0
  14. package/dist/404.bundle.0f7a500421f246153d89.js +706 -0
  15. package/dist/50.bundle.4cb103cd20f5ffccf927.js +324 -0
  16. package/dist/5004fdc02f329ce53b69.wasm +0 -0
  17. package/dist/531.bundle.1bc152c87c7e2e987d2b.js +5935 -0
  18. package/dist/55.bundle.a5a215e13a8511f7aee7.js +685 -0
  19. package/dist/55.css +3 -0
  20. package/dist/569.bundle.d147c0aa0604f8ea2094.js +514 -0
  21. package/dist/581.bundle.646c89c5c3e3ee096363.js +508 -0
  22. package/dist/606.bundle.5d876f5f3dd8287f0a28.js +4939 -0
  23. package/dist/610.min.worker.js +2 -0
  24. package/dist/610.min.worker.js.map +1 -0
  25. package/dist/616.bundle.bec4736d8c9513e62856.js +686 -0
  26. package/dist/62ab5d58a2bea7b5a1dc.wasm +0 -0
  27. package/dist/642.bundle.030d908e22c8ff5611f3.js +169 -0
  28. package/dist/65916ef3def695744bda.wasm +0 -0
  29. package/dist/664.bundle.4792c88ae0d6d4b5ed13.js +901 -0
  30. package/dist/707.bundle.0a74aa3e61ed002eb3c6.js +9049 -0
  31. package/dist/707.css +1 -0
  32. package/dist/728.bundle.d13856835357400fef82.js +26221 -0
  33. package/dist/744.bundle.53b07e48e07a11e920ac.js +2355 -0
  34. package/dist/75788f12450d4c5ed494.wasm +0 -0
  35. package/dist/75a0c2dfe07b824c7d21.wasm +0 -0
  36. package/dist/780.bundle.f60ac1906e0ae080dee8.js +4769 -0
  37. package/dist/790.bundle.b4df2c5d78a2a565b150.js +454 -0
  38. package/dist/799.bundle.3fff638815e355b0bdfd.js +271 -0
  39. package/dist/806.css +1 -0
  40. package/dist/82.bundle.a24015533196e05d190e.js +6104 -0
  41. package/dist/917.bundle.a094ae9e9de6df4119ae.js +196 -0
  42. package/dist/926.bundle.dbc9d0e591cb9217fda2.js +72552 -0
  43. package/dist/935.bundle.deeffff0e4f7b528e3c3.js +1849 -0
  44. package/dist/945.min.worker.js +2 -0
  45. package/dist/945.min.worker.js.map +1 -0
  46. package/dist/953.bundle.c14d9eb6400f697019ee.js +449 -0
  47. package/dist/973.bundle.4100cf103686b64938d1.js +261 -0
  48. package/dist/976.bundle.2720eb892514e1818018.js +2725 -0
  49. package/dist/984.bundle.157fc66ea5040e1364af.js +1842 -0
  50. package/dist/_headers +6 -0
  51. package/dist/_redirects +6 -0
  52. package/dist/app-config.js +215 -0
  53. package/dist/app.bundle.253eeb2a7ee986e89c50.js +154621 -0
  54. package/dist/app.bundle.css +21 -0
  55. package/dist/assets/android-chrome-144x144.png +0 -0
  56. package/dist/assets/android-chrome-192x192.png +0 -0
  57. package/dist/assets/android-chrome-256x256.png +0 -0
  58. package/dist/assets/android-chrome-36x36.png +0 -0
  59. package/dist/assets/android-chrome-384x384.png +0 -0
  60. package/dist/assets/android-chrome-48x48.png +0 -0
  61. package/dist/assets/android-chrome-512x512.png +0 -0
  62. package/dist/assets/android-chrome-72x72.png +0 -0
  63. package/dist/assets/android-chrome-96x96.png +0 -0
  64. package/dist/assets/apple-touch-icon-1024x1024.png +0 -0
  65. package/dist/assets/apple-touch-icon-114x114.png +0 -0
  66. package/dist/assets/apple-touch-icon-120x120.png +0 -0
  67. package/dist/assets/apple-touch-icon-144x144.png +0 -0
  68. package/dist/assets/apple-touch-icon-152x152.png +0 -0
  69. package/dist/assets/apple-touch-icon-167x167.png +0 -0
  70. package/dist/assets/apple-touch-icon-180x180.png +0 -0
  71. package/dist/assets/apple-touch-icon-57x57.png +0 -0
  72. package/dist/assets/apple-touch-icon-60x60.png +0 -0
  73. package/dist/assets/apple-touch-icon-72x72.png +0 -0
  74. package/dist/assets/apple-touch-icon-76x76.png +0 -0
  75. package/dist/assets/apple-touch-icon-precomposed.png +0 -0
  76. package/dist/assets/apple-touch-icon.png +0 -0
  77. package/dist/assets/apple-touch-startup-image-1182x2208.png +0 -0
  78. package/dist/assets/apple-touch-startup-image-1242x2148.png +0 -0
  79. package/dist/assets/apple-touch-startup-image-1496x2048.png +0 -0
  80. package/dist/assets/apple-touch-startup-image-1536x2008.png +0 -0
  81. package/dist/assets/apple-touch-startup-image-320x460.png +0 -0
  82. package/dist/assets/apple-touch-startup-image-640x1096.png +0 -0
  83. package/dist/assets/apple-touch-startup-image-640x920.png +0 -0
  84. package/dist/assets/apple-touch-startup-image-748x1024.png +0 -0
  85. package/dist/assets/apple-touch-startup-image-750x1294.png +0 -0
  86. package/dist/assets/apple-touch-startup-image-768x1004.png +0 -0
  87. package/dist/assets/browserconfig.xml +12 -0
  88. package/dist/assets/coast-228x228.png +0 -0
  89. package/dist/assets/favicon-16x16.png +0 -0
  90. package/dist/assets/favicon-32x32.png +0 -0
  91. package/dist/assets/favicon.ico +0 -0
  92. package/dist/assets/firefox_app_128x128.png +0 -0
  93. package/dist/assets/firefox_app_512x512.png +0 -0
  94. package/dist/assets/firefox_app_60x60.png +0 -0
  95. package/dist/assets/manifest.webapp +14 -0
  96. package/dist/assets/mstile-144x144.png +0 -0
  97. package/dist/assets/mstile-150x150.png +0 -0
  98. package/dist/assets/mstile-310x150.png +0 -0
  99. package/dist/assets/mstile-310x310.png +0 -0
  100. package/dist/assets/mstile-70x70.png +0 -0
  101. package/dist/assets/yandex-browser-50x50.png +0 -0
  102. package/dist/assets/yandex-browser-manifest.json +9 -0
  103. package/dist/b6b803111e2d06a825bd.wasm +0 -0
  104. package/dist/c22b37c3488e1d6c3aa4.wasm +0 -0
  105. package/dist/cornerstoneDICOMImageLoader.min.js +2 -0
  106. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -0
  107. package/dist/dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js +12 -0
  108. package/dist/dicomMicroscopyViewer.min.js +3 -0
  109. package/dist/dicomMicroscopyViewer.min.js.LICENSE.txt +29 -0
  110. package/dist/es6-shim.min.js +12 -0
  111. package/dist/google.js +75 -0
  112. package/dist/index.html +1 -0
  113. package/dist/index.worker.ea71efba2ce63c499055.worker.js +2 -0
  114. package/dist/index.worker.ea71efba2ce63c499055.worker.js.map +1 -0
  115. package/dist/index.worker.min.worker.js +2 -0
  116. package/dist/index.worker.min.worker.js.map +1 -0
  117. package/dist/init-service-worker.js +59 -0
  118. package/dist/manifest.json +59 -0
  119. package/dist/ohif-logo-light.svg +15 -0
  120. package/dist/ohif-logo.svg +15 -0
  121. package/dist/oidc-client.min.js +46 -0
  122. package/dist/polyfill.min.js +1 -0
  123. package/dist/silent-refresh.html +16 -0
  124. package/dist/sw.js +56 -0
  125. package/package.json +24 -23
@@ -0,0 +1,2725 @@
1
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[976],{
2
+
3
+ /***/ 77594:
4
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5
+
6
+ "use strict";
7
+ // ESM COMPAT FLAG
8
+ __webpack_require__.r(__webpack_exports__);
9
+
10
+ // EXPORTS
11
+ __webpack_require__.d(__webpack_exports__, {
12
+ "default": () => (/* binding */ dicom_microscopy_src)
13
+ });
14
+
15
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/package.json
16
+ const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-dicom-microscopy"}');
17
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/id.js
18
+
19
+ const id = package_namespaceObject.u2;
20
+
21
+ // EXTERNAL MODULE: ../../../node_modules/react/index.js
22
+ var react = __webpack_require__(32735);
23
+ // EXTERNAL MODULE: ../../ui/src/index.js + 449 modules
24
+ var src = __webpack_require__(43803);
25
+ // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
26
+ var core_src = __webpack_require__(48501);
27
+ // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
28
+ var es = __webpack_require__(21572);
29
+ // EXTERNAL MODULE: ../../../node_modules/mathjs/lib/esm/index.js + 972 modules
30
+ var esm = __webpack_require__(29926);
31
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/coordinateFormatScoord3d2Geometry.js
32
+
33
+
34
+ // TODO -> This is pulled out of some internal logic from Dicom Microscopy Viewer,
35
+ // We should likely just expose this there.
36
+
37
+ function coordinateFormatScoord3d2Geometry(coordinates, pyramid) {
38
+ let transform = false;
39
+ if (!Array.isArray(coordinates[0])) {
40
+ coordinates = [coordinates];
41
+ transform = true;
42
+ }
43
+ const metadata = pyramid[pyramid.length - 1];
44
+ const orientation = metadata.ImageOrientationSlide;
45
+ const spacing = _getPixelSpacing(metadata);
46
+ const origin = metadata.TotalPixelMatrixOriginSequence[0];
47
+ const offset = [Number(origin.XOffsetInSlideCoordinateSystem), Number(origin.YOffsetInSlideCoordinateSystem)];
48
+ coordinates = coordinates.map(c => {
49
+ const slideCoord = [c[0], c[1]];
50
+ const pixelCoord = mapSlideCoord2PixelCoord({
51
+ offset,
52
+ orientation,
53
+ spacing,
54
+ point: slideCoord
55
+ });
56
+ return [pixelCoord[0], -(pixelCoord[1] + 1), 0];
57
+ });
58
+ if (transform) {
59
+ return coordinates[0];
60
+ }
61
+ return coordinates;
62
+ }
63
+ function _getPixelSpacing(metadata) {
64
+ if (metadata.PixelSpacing) return metadata.PixelSpacing;
65
+ const functionalGroup = metadata.SharedFunctionalGroupsSequence[0];
66
+ const pixelMeasures = functionalGroup.PixelMeasuresSequence[0];
67
+ return pixelMeasures.PixelSpacing;
68
+ }
69
+ function mapSlideCoord2PixelCoord(options) {
70
+ // X and Y Offset in Slide Coordinate System
71
+ if (!('offset' in options)) {
72
+ throw new Error('Option "offset" is required.');
73
+ }
74
+ if (!Array.isArray(options.offset)) {
75
+ throw new Error('Option "offset" must be an array.');
76
+ }
77
+ if (options.offset.length !== 2) {
78
+ throw new Error('Option "offset" must be an array with 2 elements.');
79
+ }
80
+ const offset = options.offset;
81
+
82
+ // Image Orientation Slide with direction cosines for Row and Column direction
83
+ if (!('orientation' in options)) {
84
+ throw new Error('Option "orientation" is required.');
85
+ }
86
+ if (!Array.isArray(options.orientation)) {
87
+ throw new Error('Option "orientation" must be an array.');
88
+ }
89
+ if (options.orientation.length !== 6) {
90
+ throw new Error('Option "orientation" must be an array with 6 elements.');
91
+ }
92
+ const orientation = options.orientation;
93
+
94
+ // Pixel Spacing along the Row and Column direction
95
+ if (!('spacing' in options)) {
96
+ throw new Error('Option "spacing" is required.');
97
+ }
98
+ if (!Array.isArray(options.spacing)) {
99
+ throw new Error('Option "spacing" must be an array.');
100
+ }
101
+ if (options.spacing.length !== 2) {
102
+ throw new Error('Option "spacing" must be an array with 2 elements.');
103
+ }
104
+ const spacing = options.spacing;
105
+
106
+ // X and Y coordinate in the Slide Coordinate System
107
+ if (!('point' in options)) {
108
+ throw new Error('Option "point" is required.');
109
+ }
110
+ if (!Array.isArray(options.point)) {
111
+ throw new Error('Option "point" must be an array.');
112
+ }
113
+ if (options.point.length !== 2) {
114
+ throw new Error('Option "point" must be an array with 2 elements.');
115
+ }
116
+ const point = options.point;
117
+ const m = [[orientation[0] * spacing[1], orientation[3] * spacing[0], offset[0]], [orientation[1] * spacing[1], orientation[4] * spacing[0], offset[1]], [0, 0, 1]];
118
+ const mInverted = (0,esm/* inv */.JBn)(m);
119
+ const vSlide = [[point[0]], [point[1]], [1]];
120
+ const vImage = (0,esm/* multiply */.JpY)(mInverted, vSlide);
121
+ const row = Number(vImage[1][0].toFixed(4));
122
+ const col = Number(vImage[0][0].toFixed(4));
123
+ return [col, row];
124
+ }
125
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/styles.js
126
+ const defaultFill = {
127
+ color: 'rgba(255,255,255,0.4)'
128
+ };
129
+ const emptyFill = {
130
+ color: 'rgba(255,255,255,0.0)'
131
+ };
132
+ const defaultStroke = {
133
+ color: 'rgb(0,255,0)',
134
+ width: 1.5
135
+ };
136
+ const activeStroke = {
137
+ color: 'rgb(255,255,0)',
138
+ width: 1.5
139
+ };
140
+ const defaultStyle = {
141
+ image: {
142
+ circle: {
143
+ fill: defaultFill,
144
+ stroke: activeStroke,
145
+ radius: 5
146
+ }
147
+ },
148
+ fill: defaultFill,
149
+ stroke: activeStroke
150
+ };
151
+ const emptyStyle = {
152
+ image: {
153
+ circle: {
154
+ fill: emptyFill,
155
+ stroke: defaultStroke,
156
+ radius: 5
157
+ }
158
+ },
159
+ fill: emptyFill,
160
+ stroke: defaultStroke
161
+ };
162
+ const styles = {
163
+ active: defaultStyle,
164
+ default: emptyStyle
165
+ };
166
+ /* harmony default export */ const utils_styles = (styles);
167
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/tools/viewerManager.js
168
+
169
+
170
+
171
+
172
+ // Events from the third-party viewer
173
+ const ApiEvents = {
174
+ /** Triggered when a ROI was added. */
175
+ ROI_ADDED: 'dicommicroscopyviewer_roi_added',
176
+ /** Triggered when a ROI was modified. */
177
+ ROI_MODIFIED: 'dicommicroscopyviewer_roi_modified',
178
+ /** Triggered when a ROI was removed. */
179
+ ROI_REMOVED: 'dicommicroscopyviewer_roi_removed',
180
+ /** Triggered when a ROI was drawn. */
181
+ ROI_DRAWN: `dicommicroscopyviewer_roi_drawn`,
182
+ /** Triggered when a ROI was selected. */
183
+ ROI_SELECTED: `dicommicroscopyviewer_roi_selected`,
184
+ /** Triggered when a viewport move has started. */
185
+ MOVE_STARTED: `dicommicroscopyviewer_move_started`,
186
+ /** Triggered when a viewport move has ended. */
187
+ MOVE_ENDED: `dicommicroscopyviewer_move_ended`,
188
+ /** Triggered when a loading of data has started. */
189
+ LOADING_STARTED: `dicommicroscopyviewer_loading_started`,
190
+ /** Triggered when a loading of data has ended. */
191
+ LOADING_ENDED: `dicommicroscopyviewer_loading_ended`,
192
+ /** Triggered when an error occurs during loading of data. */
193
+ LOADING_ERROR: `dicommicroscopyviewer_loading_error`,
194
+ /* Triggered when the loading of an image tile has started. */
195
+ FRAME_LOADING_STARTED: `dicommicroscopyviewer_frame_loading_started`,
196
+ /* Triggered when the loading of an image tile has ended. */
197
+ FRAME_LOADING_ENDED: `dicommicroscopyviewer_frame_loading_ended`,
198
+ /* Triggered when the error occurs during loading of an image tile. */
199
+ FRAME_LOADING_ERROR: `dicommicroscopyviewer_frame_loading_ended`
200
+ };
201
+ const EVENTS = {
202
+ ADDED: 'added',
203
+ MODIFIED: 'modified',
204
+ REMOVED: 'removed',
205
+ UPDATED: 'updated',
206
+ SELECTED: 'selected'
207
+ };
208
+
209
+ /**
210
+ * ViewerManager encapsulates the complexity of the third-party viewer and
211
+ * expose only the features/behaviors that are relevant to the application
212
+ */
213
+ class ViewerManager extends core_src/* PubSubService */.hC {
214
+ constructor(viewer, viewportIndex, container, studyInstanceUID, seriesInstanceUID) {
215
+ super(EVENTS);
216
+ this.viewer = viewer;
217
+ this.viewportIndex = viewportIndex;
218
+ this.container = container;
219
+ this.studyInstanceUID = studyInstanceUID;
220
+ this.seriesInstanceUID = seriesInstanceUID;
221
+ this.onRoiAdded = this.roiAddedHandler.bind(this);
222
+ this.onRoiModified = this.roiModifiedHandler.bind(this);
223
+ this.onRoiRemoved = this.roiRemovedHandler.bind(this);
224
+ this.onRoiSelected = this.roiSelectedHandler.bind(this);
225
+ this.contextMenuCallback = () => {};
226
+
227
+ // init symbols
228
+ const symbols = Object.getOwnPropertySymbols(this.viewer);
229
+ this._drawingSource = symbols.find(p => p.description === 'drawingSource');
230
+ this._pyramid = symbols.find(p => p.description === 'pyramid');
231
+ this._map = symbols.find(p => p.description === 'map');
232
+ this._affine = symbols.find(p => p.description === 'affine');
233
+ this.registerEvents();
234
+ this.activateDefaultInteractions();
235
+ }
236
+ addContextMenuCallback(callback) {
237
+ this.contextMenuCallback = callback;
238
+ }
239
+
240
+ /**
241
+ * Destroys this managed viewer instance, clearing all the event handlers
242
+ */
243
+ destroy() {
244
+ this.unregisterEvents();
245
+ }
246
+
247
+ /**
248
+ * This is to overrides the _broadcastEvent method of PubSubService and always
249
+ * send the ROI graphic object and this managed viewer instance.
250
+ * Due to the way that PubSubService is written, the same name override of the
251
+ * function doesn't work.
252
+ *
253
+ * @param {String} key key Subscription key
254
+ * @param {Object} roiGraphic ROI graphic object created by the third-party API
255
+ */
256
+ publish(key, roiGraphic) {
257
+ this._broadcastEvent(key, {
258
+ roiGraphic,
259
+ managedViewer: this
260
+ });
261
+ }
262
+
263
+ /**
264
+ * Registers all the relevant event handlers for the third-party API
265
+ */
266
+ registerEvents() {
267
+ this.container.addEventListener(ApiEvents.ROI_ADDED, this.onRoiAdded);
268
+ this.container.addEventListener(ApiEvents.ROI_MODIFIED, this.onRoiModified);
269
+ this.container.addEventListener(ApiEvents.ROI_REMOVED, this.onRoiRemoved);
270
+ this.container.addEventListener(ApiEvents.ROI_SELECTED, this.onRoiSelected);
271
+ }
272
+
273
+ /**
274
+ * Cleares all the relevant event handlers for the third-party API
275
+ */
276
+ unregisterEvents() {
277
+ this.container.removeEventListener(ApiEvents.ROI_ADDED, this.onRoiAdded);
278
+ this.container.removeEventListener(ApiEvents.ROI_MODIFIED, this.onRoiModified);
279
+ this.container.removeEventListener(ApiEvents.ROI_REMOVED, this.onRoiRemoved);
280
+ this.container.removeEventListener(ApiEvents.ROI_SELECTED, this.onRoiSelected);
281
+ }
282
+
283
+ /**
284
+ * Handles the ROI_ADDED event triggered by the third-party API
285
+ *
286
+ * @param {Event} event Event triggered by the third-party API
287
+ */
288
+ roiAddedHandler(event) {
289
+ const roiGraphic = event.detail.payload;
290
+ this.publish(EVENTS.ADDED, roiGraphic);
291
+ this.publish(EVENTS.UPDATED, roiGraphic);
292
+ }
293
+
294
+ /**
295
+ * Handles the ROI_MODIFIED event triggered by the third-party API
296
+ *
297
+ * @param {Event} event Event triggered by the third-party API
298
+ */
299
+ roiModifiedHandler(event) {
300
+ const roiGraphic = event.detail.payload;
301
+ this.publish(EVENTS.MODIFIED, roiGraphic);
302
+ this.publish(EVENTS.UPDATED, roiGraphic);
303
+ }
304
+
305
+ /**
306
+ * Handles the ROI_REMOVED event triggered by the third-party API
307
+ *
308
+ * @param {Event} event Event triggered by the third-party API
309
+ */
310
+ roiRemovedHandler(event) {
311
+ const roiGraphic = event.detail.payload;
312
+ this.publish(EVENTS.REMOVED, roiGraphic);
313
+ this.publish(EVENTS.UPDATED, roiGraphic);
314
+ }
315
+
316
+ /**
317
+ * Handles the ROI_SELECTED event triggered by the third-party API
318
+ *
319
+ * @param {Event} event Event triggered by the third-party API
320
+ */
321
+ roiSelectedHandler(event) {
322
+ const roiGraphic = event.detail.payload;
323
+ this.publish(EVENTS.SELECTED, roiGraphic);
324
+ }
325
+
326
+ /**
327
+ * Run the given callback operation without triggering any events for this
328
+ * instance, so subscribers will not be affected
329
+ *
330
+ * @param {Function} callback Callback that will run sinlently
331
+ */
332
+ runSilently(callback) {
333
+ this.unregisterEvents();
334
+ callback();
335
+ this.registerEvents();
336
+ }
337
+
338
+ /**
339
+ * Removes all the ROI graphics from the third-party API
340
+ */
341
+ clearRoiGraphics() {
342
+ this.runSilently(() => this.viewer.removeAllROIs());
343
+ }
344
+ showROIs() {
345
+ this.viewer.showROIs();
346
+ }
347
+ hideROIs() {
348
+ this.viewer.hideROIs();
349
+ }
350
+
351
+ /**
352
+ * Adds the given ROI graphic into the third-party API
353
+ *
354
+ * @param {Object} roiGraphic ROI graphic object to be added
355
+ */
356
+ addRoiGraphic(roiGraphic) {
357
+ this.runSilently(() => this.viewer.addROI(roiGraphic, utils_styles["default"]));
358
+ }
359
+
360
+ /**
361
+ * Adds the given ROI graphic into the third-party API, and also add a label.
362
+ * Used for importing from SR.
363
+ *
364
+ * @param {Object} roiGraphic ROI graphic object to be added.
365
+ * @param {String} label The label of the annotation.
366
+ */
367
+ addRoiGraphicWithLabel(roiGraphic, label) {
368
+ // NOTE: Dicom Microscopy Viewer will override styles for "Text" evalutations
369
+ // to hide all other geometries, we are not going to use its label.
370
+ // if (label) {
371
+ // if (!roiGraphic.properties) roiGraphic.properties = {};
372
+ // roiGraphic.properties.label = label;
373
+ // }
374
+ this.runSilently(() => this.viewer.addROI(roiGraphic, utils_styles["default"]));
375
+ this._broadcastEvent(EVENTS.ADDED, {
376
+ roiGraphic,
377
+ managedViewer: this,
378
+ label
379
+ });
380
+ }
381
+
382
+ /**
383
+ * Sets ROI style
384
+ *
385
+ * @param {String} uid ROI graphic UID to be styled
386
+ * @param {object} styleOptions - Style options
387
+ * @param {object} styleOptions.stroke - Style options for the outline of the geometry
388
+ * @param {number[]} styleOptions.stroke.color - RGBA color of the outline
389
+ * @param {number} styleOptions.stroke.width - Width of the outline
390
+ * @param {object} styleOptions.fill - Style options for body the geometry
391
+ * @param {number[]} styleOptions.fill.color - RGBA color of the body
392
+ * @param {object} styleOptions.image - Style options for image
393
+ */
394
+ setROIStyle(uid, styleOptions) {
395
+ this.viewer.setROIStyle(uid, styleOptions);
396
+ }
397
+
398
+ /**
399
+ * Removes the ROI graphic with the given UID from the third-party API
400
+ *
401
+ * @param {String} uid ROI graphic UID to be removed
402
+ */
403
+ removeRoiGraphic(uid) {
404
+ this.viewer.removeROI(uid);
405
+ }
406
+
407
+ /**
408
+ * Update properties of regions of interest.
409
+ *
410
+ * @param {object} roi - ROI to be updated
411
+ * @param {string} roi.uid - Unique identifier of the region of interest
412
+ * @param {object} roi.properties - ROI properties
413
+ * @returns {void}
414
+ */
415
+ updateROIProperties(_ref) {
416
+ let {
417
+ uid,
418
+ properties
419
+ } = _ref;
420
+ this.viewer.updateROI({
421
+ uid,
422
+ properties
423
+ });
424
+ }
425
+
426
+ /**
427
+ * Toggles overview map
428
+ *
429
+ * @returns {void}
430
+ */
431
+ toggleOverviewMap() {
432
+ this.viewer.toggleOverviewMap();
433
+ }
434
+
435
+ /**
436
+ * Activates the viewer default interactions
437
+ * @returns {void}
438
+ */
439
+ activateDefaultInteractions() {
440
+ /** Disable browser's native context menu inside the canvas */
441
+ document.querySelector('.DicomMicroscopyViewer').addEventListener('contextmenu', event => {
442
+ event.preventDefault();
443
+ // comment out when context menu for microscopy is enabled
444
+ // if (typeof this.contextMenuCallback === 'function') {
445
+ // this.contextMenuCallback(event);
446
+ // }
447
+ }, false);
448
+ const defaultInteractions = [['dragPan', {
449
+ bindings: {
450
+ mouseButtons: ['middle']
451
+ }
452
+ }], ['dragZoom', {
453
+ bindings: {
454
+ mouseButtons: ['right']
455
+ }
456
+ }], ['modify', {}]];
457
+ this.activateInteractions(defaultInteractions);
458
+ }
459
+
460
+ /**
461
+ * Activates interactions
462
+ * @param {Array} interactions Interactions to be activated
463
+ * @returns {void}
464
+ */
465
+ activateInteractions(interactions) {
466
+ const interactionsMap = {
467
+ draw: activate => activate ? 'activateDrawInteraction' : 'deactivateDrawInteraction',
468
+ modify: activate => activate ? 'activateModifyInteraction' : 'deactivateModifyInteraction',
469
+ translate: activate => activate ? 'activateTranslateInteraction' : 'deactivateTranslateInteraction',
470
+ snap: activate => activate ? 'activateSnapInteraction' : 'deactivateSnapInteraction',
471
+ dragPan: activate => activate ? 'activateDragPanInteraction' : 'deactivateDragPanInteraction',
472
+ dragZoom: activate => activate ? 'activateDragZoomInteraction' : 'deactivateDragZoomInteraction',
473
+ select: activate => activate ? 'activateSelectInteraction' : 'deactivateSelectInteraction'
474
+ };
475
+ const availableInteractionsName = Object.keys(interactionsMap);
476
+ availableInteractionsName.forEach(availableInteractionName => {
477
+ const interaction = interactions.find(interaction => interaction[0] === availableInteractionName);
478
+ if (!interaction) {
479
+ const deactivateInteractionMethod = interactionsMap[availableInteractionName](false);
480
+ this.viewer[deactivateInteractionMethod]();
481
+ } else {
482
+ const [name, config] = interaction;
483
+ const activateInteractionMethod = interactionsMap[name](true);
484
+ this.viewer[activateInteractionMethod](config);
485
+ }
486
+ });
487
+ }
488
+
489
+ /**
490
+ * Accesses the internals of third-party API and returns the OpenLayers Map
491
+ *
492
+ * @returns {Object} OpenLayers Map component instance
493
+ */
494
+ _getMapView() {
495
+ const map = this._getMap();
496
+ return map.getView();
497
+ }
498
+ _getMap() {
499
+ const symbols = Object.getOwnPropertySymbols(this.viewer);
500
+ const _map = symbols.find(s => String(s) === 'Symbol(map)');
501
+ window['map'] = this.viewer[_map];
502
+ return this.viewer[_map];
503
+ }
504
+
505
+ /**
506
+ * Returns the current state for the OpenLayers View
507
+ *
508
+ * @returns {Object} Current view state
509
+ */
510
+ getViewState() {
511
+ const view = this._getMapView();
512
+ return {
513
+ center: view.getCenter(),
514
+ resolution: view.getResolution(),
515
+ zoom: view.getZoom()
516
+ };
517
+ }
518
+
519
+ /**
520
+ * Sets the current state for the OpenLayers View
521
+ *
522
+ * @param {Object} viewState View state to be applied
523
+ */
524
+ setViewState(viewState) {
525
+ const view = this._getMapView();
526
+ view.setZoom(viewState.zoom);
527
+ view.setResolution(viewState.resolution);
528
+ view.setCenter(viewState.center);
529
+ }
530
+ setViewStateByExtent(roiAnnotation) {
531
+ const coordinates = roiAnnotation.getCoordinates();
532
+ if (Array.isArray(coordinates[0]) && !coordinates[2]) {
533
+ this._jumpToPolyline(coordinates);
534
+ } else if (Array.isArray(coordinates[0])) {
535
+ this._jumpToPolygonOrEllipse(coordinates);
536
+ } else {
537
+ this._jumpToPoint(coordinates);
538
+ }
539
+ }
540
+ _jumpToPoint(coord) {
541
+ const pyramid = this.viewer[this._pyramid].metadata;
542
+ const mappedCoord = coordinateFormatScoord3d2Geometry(coord, pyramid);
543
+ const view = this._getMapView();
544
+ view.setCenter(mappedCoord);
545
+ }
546
+ _jumpToPolyline(coord) {
547
+ const pyramid = this.viewer[this._pyramid].metadata;
548
+ const mappedCoord = coordinateFormatScoord3d2Geometry(coord, pyramid);
549
+ const view = this._getMapView();
550
+ const x = mappedCoord[0];
551
+ const y = mappedCoord[1];
552
+ const xab = (x[0] + y[0]) / 2;
553
+ const yab = (x[1] + y[1]) / 2;
554
+ const midpoint = [xab, yab];
555
+ view.setCenter(midpoint);
556
+ }
557
+ _jumpToPolygonOrEllipse(coordinates) {
558
+ const pyramid = this.viewer[this._pyramid].metadata;
559
+ let minX = Infinity;
560
+ let maxX = -Infinity;
561
+ let minY = Infinity;
562
+ let maxY = -Infinity;
563
+ coordinates.forEach(coord => {
564
+ let mappedCoord = coordinateFormatScoord3d2Geometry(coord, pyramid);
565
+ const [x, y] = mappedCoord;
566
+ if (x < minX) {
567
+ minX = x;
568
+ } else if (x > maxX) {
569
+ maxX = x;
570
+ }
571
+ if (y < minY) {
572
+ minY = y;
573
+ } else if (y > maxY) {
574
+ maxY = y;
575
+ }
576
+ });
577
+ const width = maxX - minX;
578
+ const height = maxY - minY;
579
+ minX -= 0.5 * width;
580
+ maxX += 0.5 * width;
581
+ minY -= 0.5 * height;
582
+ maxY += 0.5 * height;
583
+ const map = this._getMap();
584
+ map.getView().fit([minX, minY, maxX, maxY], map.getSize());
585
+ }
586
+ }
587
+
588
+ /* harmony default export */ const viewerManager = (ViewerManager);
589
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/areaOfPolygon.js
590
+ function areaOfPolygon(coordinates) {
591
+ // Shoelace algorithm.
592
+ const n = coordinates.length;
593
+ let area = 0.0;
594
+ let j = n - 1;
595
+ for (let i = 0; i < n; i++) {
596
+ area += (coordinates[j][0] + coordinates[i][0]) * (coordinates[j][1] - coordinates[i][1]);
597
+ j = i; // j is previous vertex to i
598
+ }
599
+
600
+ // Return absolute value of half the sum
601
+ // (The value is halved as we are summing up triangles, not rectangles).
602
+ return Math.abs(area / 2.0);
603
+ }
604
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/RoiAnnotation.js
605
+
606
+
607
+ const RoiAnnotation_EVENTS = {
608
+ LABEL_UPDATED: 'labelUpdated',
609
+ GRAPHIC_UPDATED: 'graphicUpdated',
610
+ VIEW_UPDATED: 'viewUpdated',
611
+ REMOVED: 'removed'
612
+ };
613
+
614
+ /**
615
+ * Represents a single annotation for the Microscopy Viewer
616
+ */
617
+ class RoiAnnotation extends core_src/* PubSubService */.hC {
618
+ constructor(roiGraphic, studyInstanceUID, seriesInstanceUID) {
619
+ let label = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
620
+ let viewState = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
621
+ super(RoiAnnotation_EVENTS);
622
+ this.uid = roiGraphic.uid;
623
+ this.roiGraphic = roiGraphic;
624
+ this.studyInstanceUID = studyInstanceUID;
625
+ this.seriesInstanceUID = seriesInstanceUID;
626
+ this.label = label;
627
+ this.viewState = viewState;
628
+ this.setMeasurements(roiGraphic);
629
+ }
630
+ getScoord3d() {
631
+ const roiGraphic = this.roiGraphic;
632
+ const roiGraphicSymbols = Object.getOwnPropertySymbols(roiGraphic);
633
+ const _scoord3d = roiGraphicSymbols.find(s => String(s) === 'Symbol(scoord3d)');
634
+ return roiGraphic[_scoord3d];
635
+ }
636
+ getCoordinates() {
637
+ const scoord3d = this.getScoord3d();
638
+ const scoord3dSymbols = Object.getOwnPropertySymbols(scoord3d);
639
+ const _coordinates = scoord3dSymbols.find(s => String(s) === 'Symbol(coordinates)');
640
+ const coordinates = scoord3d[_coordinates];
641
+ return coordinates;
642
+ }
643
+
644
+ /**
645
+ * When called will trigger the REMOVED event
646
+ */
647
+ destroy() {
648
+ this._broadcastEvent(RoiAnnotation_EVENTS.REMOVED, this);
649
+ }
650
+
651
+ /**
652
+ * Updates the ROI graphic for the annotation and triggers the GRAPHIC_UPDATED
653
+ * event
654
+ *
655
+ * @param {Object} roiGraphic
656
+ */
657
+ setRoiGraphic(roiGraphic) {
658
+ this.roiGraphic = roiGraphic;
659
+ this.setMeasurements();
660
+ this._broadcastEvent(RoiAnnotation_EVENTS.GRAPHIC_UPDATED, this);
661
+ }
662
+
663
+ /**
664
+ * Update ROI measurement values based on its scoord3d coordinates.
665
+ *
666
+ * @returns {void}
667
+ */
668
+ setMeasurements() {
669
+ const type = this.roiGraphic.scoord3d.graphicType;
670
+ const coordinates = this.roiGraphic.scoord3d.graphicData;
671
+ switch (type) {
672
+ case 'ELLIPSE':
673
+ // This is a circle so only need one side
674
+ const point1 = coordinates[0];
675
+ const point2 = coordinates[1];
676
+ let xLength2 = point2[0] - point1[0];
677
+ let yLength2 = point2[1] - point1[1];
678
+ xLength2 *= xLength2;
679
+ yLength2 *= yLength2;
680
+ const length = Math.sqrt(xLength2 + yLength2);
681
+ const radius = length / 2;
682
+ const areaEllipse = Math.PI * radius * radius;
683
+ this._area = areaEllipse;
684
+ this._length = undefined;
685
+ break;
686
+ case 'POLYGON':
687
+ const areaPolygon = areaOfPolygon(coordinates);
688
+ this._area = areaPolygon;
689
+ this._length = undefined;
690
+ break;
691
+ case 'POINT':
692
+ this._area = undefined;
693
+ this._length = undefined;
694
+ break;
695
+ case 'POLYLINE':
696
+ let len = 0;
697
+ for (let i = 1; i < coordinates.length; i++) {
698
+ const p1 = coordinates[i - 1];
699
+ const p2 = coordinates[i];
700
+ let xLen = p2[0] - p1[0];
701
+ let yLen = p2[1] - p1[1];
702
+ xLen *= xLen;
703
+ yLen *= yLen;
704
+ len += Math.sqrt(xLen + yLen);
705
+ }
706
+ this._area = undefined;
707
+ this._length = len;
708
+ break;
709
+ }
710
+ }
711
+
712
+ /**
713
+ * Update the OpenLayer Map's view state for the annotation and triggers the
714
+ * VIEW_UPDATED event
715
+ *
716
+ * @param {Object} viewState The new view state for the annotation
717
+ */
718
+ setViewState(viewState) {
719
+ this.viewState = viewState;
720
+ this._broadcastEvent(RoiAnnotation_EVENTS.VIEW_UPDATED, this);
721
+ }
722
+
723
+ /**
724
+ * Update the label for the annotation and triggers the LABEL_UPDATED event
725
+ *
726
+ * @param {String} label New label for the annotation
727
+ */
728
+ setLabel(label, finding) {
729
+ this.label = label || finding && finding.CodeMeaning;
730
+ this.finding = finding || {
731
+ CodingSchemeDesignator: '@ohif/extension-dicom-microscopy',
732
+ CodeValue: label,
733
+ CodeMeaning: label
734
+ };
735
+ this._broadcastEvent(RoiAnnotation_EVENTS.LABEL_UPDATED, this);
736
+ }
737
+
738
+ /**
739
+ * Returns the geometry type of the annotation concatenated with the label
740
+ * defined for the annotation.
741
+ * Difference with getDetailedLabel() is that this will return empty string for empy
742
+ * label.
743
+ *
744
+ * @returns {String} Text with geometry type and label
745
+ */
746
+ getLabel() {
747
+ const label = this.label ? `${this.label}` : '';
748
+ return label;
749
+ }
750
+
751
+ /**
752
+ * Returns the geometry type of the annotation concatenated with the label
753
+ * defined for the annotation
754
+ *
755
+ * @returns {String} Text with geometry type and label
756
+ */
757
+ getDetailedLabel() {
758
+ const label = this.label ? `${this.label}` : '(empty)';
759
+ return label;
760
+ }
761
+ getLength() {
762
+ return this._length;
763
+ }
764
+ getArea() {
765
+ return this._area;
766
+ }
767
+ }
768
+
769
+ /* harmony default export */ const utils_RoiAnnotation = (RoiAnnotation);
770
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/services/MicroscopyService.ts
771
+
772
+
773
+
774
+
775
+ const MicroscopyService_EVENTS = {
776
+ ANNOTATION_UPDATED: 'annotationUpdated',
777
+ ANNOTATION_SELECTED: 'annotationSelected',
778
+ ANNOTATION_REMOVED: 'annotationRemoved',
779
+ RELABEL: 'relabel',
780
+ DELETE: 'delete'
781
+ };
782
+
783
+ /**
784
+ * MicroscopyService is responsible to manage multiple third-party API's
785
+ * microscopy viewers expose methods to manage the interaction with these
786
+ * viewers and handle their ROI graphics to create, remove and modify the
787
+ * ROI annotations relevant to the application
788
+ */
789
+ class MicroscopyService extends core_src/* PubSubService */.hC {
790
+ constructor(serviceManager) {
791
+ super(MicroscopyService_EVENTS);
792
+ this.serviceManager = void 0;
793
+ this.managedViewers = new Set();
794
+ this.roiUids = new Set();
795
+ this.annotations = {};
796
+ this.selectedAnnotation = null;
797
+ this.pendingFocus = false;
798
+ this.serviceManager = serviceManager;
799
+ this._onRoiAdded = this._onRoiAdded.bind(this);
800
+ this._onRoiModified = this._onRoiModified.bind(this);
801
+ this._onRoiRemoved = this._onRoiRemoved.bind(this);
802
+ this._onRoiUpdated = this._onRoiUpdated.bind(this);
803
+ this._onRoiSelected = this._onRoiSelected.bind(this);
804
+ this.isROIsVisible = true;
805
+ }
806
+
807
+ /**
808
+ * Cleares all the annotations and managed viewers, setting the manager state
809
+ * to its initial state
810
+ */
811
+ clear() {
812
+ this.managedViewers.forEach(managedViewer => managedViewer.destroy());
813
+ this.managedViewers.clear();
814
+ for (var key in this.annotations) {
815
+ delete this.annotations[key];
816
+ }
817
+ this.roiUids.clear();
818
+ this.selectedAnnotation = null;
819
+ this.pendingFocus = false;
820
+ }
821
+ clearAnnotations() {
822
+ Object.keys(this.annotations).forEach(uid => {
823
+ this.removeAnnotation(this.annotations[uid]);
824
+ });
825
+ }
826
+
827
+ /**
828
+ * Observes when a ROI graphic is added, creating the correspondent annotation
829
+ * with the current graphic and view state.
830
+ * Creates a subscription for label updating for the created annotation and
831
+ * publishes an ANNOTATION_UPDATED event when it happens.
832
+ * Also triggers the relabel process after the graphic is placed.
833
+ *
834
+ * @param {Object} data The published data
835
+ * @param {Object} data.roiGraphic The added ROI graphic object
836
+ * @param {ViewerManager} data.managedViewer The origin viewer for the event
837
+ */
838
+ _onRoiAdded(data) {
839
+ const {
840
+ roiGraphic,
841
+ managedViewer,
842
+ label
843
+ } = data;
844
+ const {
845
+ studyInstanceUID,
846
+ seriesInstanceUID
847
+ } = managedViewer;
848
+ const viewState = managedViewer.getViewState();
849
+ const roiAnnotation = new utils_RoiAnnotation(roiGraphic, studyInstanceUID, seriesInstanceUID, '', viewState);
850
+ this.roiUids.add(roiGraphic.uid);
851
+ this.annotations[roiGraphic.uid] = roiAnnotation;
852
+ roiAnnotation.subscribe(RoiAnnotation_EVENTS.LABEL_UPDATED, () => {
853
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_UPDATED, roiAnnotation);
854
+ });
855
+ if (label !== undefined) {
856
+ roiAnnotation.setLabel(label);
857
+ } else {
858
+ const onRelabel = item => managedViewer.updateROIProperties({
859
+ uid: roiGraphic.uid,
860
+ properties: {
861
+ label: item.label,
862
+ finding: item.finding
863
+ }
864
+ });
865
+ this.triggerRelabel(roiAnnotation, true, onRelabel);
866
+ }
867
+ }
868
+
869
+ /**
870
+ * Observes when a ROI graphic is modified, updating the correspondent
871
+ * annotation with the current graphic and view state.
872
+ *
873
+ * @param {Object} data The published data
874
+ * @param {Object} data.roiGraphic The modified ROI graphic object
875
+ */
876
+ _onRoiModified(data) {
877
+ const {
878
+ roiGraphic,
879
+ managedViewer
880
+ } = data;
881
+ const roiAnnotation = this.getAnnotation(roiGraphic.uid);
882
+ if (!roiAnnotation) return;
883
+ roiAnnotation.setRoiGraphic(roiGraphic);
884
+ roiAnnotation.setViewState(managedViewer.getViewState());
885
+ }
886
+
887
+ /**
888
+ * Observes when a ROI graphic is removed, reflecting the removal in the
889
+ * annotations' state.
890
+ *
891
+ * @param {Object} data The published data
892
+ * @param {Object} data.roiGraphic The removed ROI graphic object
893
+ */
894
+ _onRoiRemoved(data) {
895
+ const {
896
+ roiGraphic
897
+ } = data;
898
+ this.roiUids.delete(roiGraphic.uid);
899
+ this.annotations[roiGraphic.uid].destroy();
900
+ delete this.annotations[roiGraphic.uid];
901
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_REMOVED, roiGraphic);
902
+ }
903
+
904
+ /**
905
+ * Observes any changes on ROI graphics and synchronize all the managed
906
+ * viewers to reflect those changes.
907
+ * Also publishes an ANNOTATION_UPDATED event to notify the subscribers.
908
+ *
909
+ * @param {Object} data The published data
910
+ * @param {Object} data.roiGraphic The added ROI graphic object
911
+ * @param {ViewerManager} data.managedViewer The origin viewer for the event
912
+ */
913
+ _onRoiUpdated(data) {
914
+ const {
915
+ roiGraphic,
916
+ managedViewer
917
+ } = data;
918
+ this.synchronizeViewers(managedViewer);
919
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_UPDATED, this.getAnnotation(roiGraphic.uid));
920
+ }
921
+
922
+ /**
923
+ * Observes when an ROI is selected.
924
+ * Also publishes an ANNOTATION_SELECTED event to notify the subscribers.
925
+ *
926
+ * @param {Object} data The published data
927
+ * @param {Object} data.roiGraphic The added ROI graphic object
928
+ * @param {ViewerManager} data.managedViewer The origin viewer for the event
929
+ */
930
+ _onRoiSelected(data) {
931
+ const {
932
+ roiGraphic
933
+ } = data;
934
+ const selectedAnnotation = this.getAnnotation(roiGraphic.uid);
935
+ if (selectedAnnotation && selectedAnnotation !== this.getSelectedAnnotation()) {
936
+ if (this.selectedAnnotation) this.clearSelection();
937
+ this.selectedAnnotation = selectedAnnotation;
938
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_SELECTED, selectedAnnotation);
939
+ }
940
+ }
941
+
942
+ /**
943
+ * Creates the subscriptions for the managed viewer being added
944
+ *
945
+ * @param {ViewerManager} managedViewer The viewer being added
946
+ */
947
+ _addManagedViewerSubscriptions(managedViewer) {
948
+ managedViewer._roiAddedSubscription = managedViewer.subscribe(EVENTS.ADDED, this._onRoiAdded);
949
+ managedViewer._roiModifiedSubscription = managedViewer.subscribe(EVENTS.MODIFIED, this._onRoiModified);
950
+ managedViewer._roiRemovedSubscription = managedViewer.subscribe(EVENTS.REMOVED, this._onRoiRemoved);
951
+ managedViewer._roiUpdatedSubscription = managedViewer.subscribe(EVENTS.UPDATED, this._onRoiUpdated);
952
+ managedViewer._roiSelectedSubscription = managedViewer.subscribe(EVENTS.UPDATED, this._onRoiSelected);
953
+ }
954
+
955
+ /**
956
+ * Removes the subscriptions for the managed viewer being removed
957
+ *
958
+ * @param {ViewerManager} managedViewer The viewer being removed
959
+ */
960
+ _removeManagedViewerSubscriptions(managedViewer) {
961
+ managedViewer._roiAddedSubscription && managedViewer._roiAddedSubscription.unsubscribe();
962
+ managedViewer._roiModifiedSubscription && managedViewer._roiModifiedSubscription.unsubscribe();
963
+ managedViewer._roiRemovedSubscription && managedViewer._roiRemovedSubscription.unsubscribe();
964
+ managedViewer._roiUpdatedSubscription && managedViewer._roiUpdatedSubscription.unsubscribe();
965
+ managedViewer._roiSelectedSubscription && managedViewer._roiSelectedSubscription.unsubscribe();
966
+ managedViewer._roiAddedSubscription = null;
967
+ managedViewer._roiModifiedSubscription = null;
968
+ managedViewer._roiRemovedSubscription = null;
969
+ managedViewer._roiUpdatedSubscription = null;
970
+ managedViewer._roiSelectedSubscription = null;
971
+ }
972
+
973
+ /**
974
+ * Returns the managed viewers that are displaying the image with the given
975
+ * study and series UIDs
976
+ *
977
+ * @param {String} studyInstanceUID UID for the study
978
+ * @param {String} seriesInstanceUID UID for the series
979
+ *
980
+ * @returns {Array} The managed viewers for the given series UID
981
+ */
982
+ _getManagedViewersForSeries(studyInstanceUID, seriesInstanceUID) {
983
+ const filter = managedViewer => managedViewer.studyInstanceUID === studyInstanceUID && managedViewer.seriesInstanceUID === seriesInstanceUID;
984
+ return Array.from(this.managedViewers).filter(filter);
985
+ }
986
+
987
+ /**
988
+ * Returns the managed viewers that are displaying the image with the given
989
+ * study UID
990
+ *
991
+ * @param {String} studyInstanceUID UID for the study
992
+ *
993
+ * @returns {Array} The managed viewers for the given series UID
994
+ */
995
+ getManagedViewersForStudy(studyInstanceUID) {
996
+ const filter = managedViewer => managedViewer.studyInstanceUID === studyInstanceUID;
997
+ return Array.from(this.managedViewers).filter(filter);
998
+ }
999
+
1000
+ /**
1001
+ * Restores the created annotations for the viewer being added
1002
+ *
1003
+ * @param {ViewerManager} managedViewer The viewer being added
1004
+ */
1005
+ _restoreAnnotations(managedViewer) {
1006
+ const {
1007
+ studyInstanceUID,
1008
+ seriesInstanceUID
1009
+ } = managedViewer;
1010
+ const annotations = this.getAnnotationsForSeries(studyInstanceUID, seriesInstanceUID);
1011
+ annotations.forEach(roiAnnotation => {
1012
+ managedViewer.addRoiGraphic(roiAnnotation.roiGraphic);
1013
+ });
1014
+ }
1015
+
1016
+ /**
1017
+ * Creates a managed viewer instance for the given thrid-party API's viewer.
1018
+ * Restores existing annotations for the given study/series.
1019
+ * Adds event subscriptions for the viewer being added.
1020
+ * Focuses the selected annotation when the viewer is being loaded into the
1021
+ * active viewport.
1022
+ *
1023
+ * @param {Object} viewer Third-party viewer API's object to be managed
1024
+ * @param {Number} viewportIndex The index of the viewport to load the viewer
1025
+ * @param {HTMLElement} container The DOM element where it will be renderd
1026
+ * @param {String} studyInstanceUID The study UID of the loaded image
1027
+ * @param {String} seriesInstanceUID The series UID of the loaded image
1028
+ * @param {Array} displaySets All displaySets related to the same StudyInstanceUID
1029
+ *
1030
+ * @returns {ViewerManager} managed viewer
1031
+ */
1032
+ addViewer(viewer, viewportIndex, container, studyInstanceUID, seriesInstanceUID) {
1033
+ const managedViewer = new viewerManager(viewer, viewportIndex, container, studyInstanceUID, seriesInstanceUID);
1034
+ this._restoreAnnotations(managedViewer);
1035
+ viewer._manager = managedViewer;
1036
+ this.managedViewers.add(managedViewer);
1037
+
1038
+ // this._potentiallyLoadSR(studyInstanceUID, displaySets);
1039
+ this._addManagedViewerSubscriptions(managedViewer);
1040
+ if (this.pendingFocus) {
1041
+ this.pendingFocus = false;
1042
+ this.focusAnnotation(this.selectedAnnotation, viewportIndex);
1043
+ }
1044
+ return managedViewer;
1045
+ }
1046
+ _potentiallyLoadSR(StudyInstanceUID, displaySets) {
1047
+ const studyMetadata = core_src.DicomMetadataStore.getStudy(StudyInstanceUID);
1048
+ const smDisplaySet = displaySets.find(ds => ds.Modality === 'SM');
1049
+ const {
1050
+ FrameOfReferenceUID,
1051
+ othersFrameOfReferenceUID
1052
+ } = smDisplaySet;
1053
+ if (!studyMetadata) {
1054
+ return;
1055
+ }
1056
+ let derivedDisplaySets = FrameOfReferenceUID ? displaySets.filter(ds => ds.ReferencedFrameOfReferenceUID === FrameOfReferenceUID ||
1057
+ // sometimes each depth instance has the different FrameOfReferenceID
1058
+ othersFrameOfReferenceUID.includes(ds.ReferencedFrameOfReferenceUID)) : [];
1059
+ if (!derivedDisplaySets.length) {
1060
+ return;
1061
+ }
1062
+ derivedDisplaySets = derivedDisplaySets.filter(ds => ds.Modality === 'SR');
1063
+ if (derivedDisplaySets.some(ds => ds.isLoaded === true)) {
1064
+ // Don't auto load
1065
+ return;
1066
+ }
1067
+
1068
+ // find most recent and load it.
1069
+ let recentDateTime = 0;
1070
+ let recentDisplaySet = derivedDisplaySets[0];
1071
+ derivedDisplaySets.forEach(ds => {
1072
+ const dateTime = Number(`${ds.SeriesDate}${ds.SeriesTime}`);
1073
+ if (dateTime > recentDateTime) {
1074
+ recentDateTime = dateTime;
1075
+ recentDisplaySet = ds;
1076
+ }
1077
+ });
1078
+ recentDisplaySet.isLoading = true;
1079
+ recentDisplaySet.load(smDisplaySet);
1080
+ }
1081
+
1082
+ /**
1083
+ * Removes the given third-party viewer API's object from the managed viewers
1084
+ * and cleares all its event subscriptions
1085
+ *
1086
+ * @param {Object} viewer Third-party viewer API's object to be removed
1087
+ */
1088
+ removeViewer(viewer) {
1089
+ const managedViewer = viewer._manager;
1090
+ this._removeManagedViewerSubscriptions(managedViewer);
1091
+ managedViewer.destroy();
1092
+ this.managedViewers.delete(managedViewer);
1093
+ }
1094
+
1095
+ /**
1096
+ * Toggle ROIs visibility
1097
+ */
1098
+ toggleROIsVisibility() {
1099
+ this.isROIsVisible ? this.hideROIs() : this.showROIs;
1100
+ this.isROIsVisible = !this.isROIsVisible;
1101
+ }
1102
+
1103
+ /**
1104
+ * Hide all ROIs
1105
+ */
1106
+ hideROIs() {
1107
+ this.managedViewers.forEach(mv => mv.hideROIs());
1108
+ }
1109
+
1110
+ /** Show all ROIs */
1111
+ showROIs() {
1112
+ this.managedViewers.forEach(mv => mv.showROIs());
1113
+ }
1114
+
1115
+ /**
1116
+ * Returns a RoiAnnotation instance for the given ROI UID
1117
+ *
1118
+ * @param {String} uid UID of the annotation
1119
+ *
1120
+ * @returns {RoiAnnotation} The RoiAnnotation instance found for the given UID
1121
+ */
1122
+ getAnnotation(uid) {
1123
+ return this.annotations[uid];
1124
+ }
1125
+
1126
+ /**
1127
+ * Returns all the RoiAnnotation instances being managed
1128
+ *
1129
+ * @returns {Array} All RoiAnnotation instances
1130
+ */
1131
+ getAnnotations() {
1132
+ const annotations = [];
1133
+ Object.keys(this.annotations).forEach(uid => {
1134
+ annotations.push(this.getAnnotation(uid));
1135
+ });
1136
+ return annotations;
1137
+ }
1138
+
1139
+ /**
1140
+ * Returns the RoiAnnotation instances registered with the given study UID
1141
+ *
1142
+ * @param {String} studyInstanceUID UID for the study
1143
+ */
1144
+ getAnnotationsForStudy(studyInstanceUID) {
1145
+ const filter = a => a.studyInstanceUID === studyInstanceUID;
1146
+ return this.getAnnotations().filter(filter);
1147
+ }
1148
+
1149
+ /**
1150
+ * Returns the RoiAnnotation instances registered with the given study and
1151
+ * series UIDs
1152
+ *
1153
+ * @param {String} studyInstanceUID UID for the study
1154
+ * @param {String} seriesInstanceUID UID for the series
1155
+ */
1156
+ getAnnotationsForSeries(studyInstanceUID, seriesInstanceUID) {
1157
+ const filter = annotation => annotation.studyInstanceUID === studyInstanceUID && annotation.seriesInstanceUID === seriesInstanceUID;
1158
+ return this.getAnnotations().filter(filter);
1159
+ }
1160
+
1161
+ /**
1162
+ * Returns the selected RoiAnnotation instance or null if none is selected
1163
+ *
1164
+ * @returns {RoiAnnotation} The selected RoiAnnotation instance
1165
+ */
1166
+ getSelectedAnnotation() {
1167
+ return this.selectedAnnotation;
1168
+ }
1169
+
1170
+ /**
1171
+ * Clear current RoiAnnotation selection
1172
+ */
1173
+ clearSelection() {
1174
+ if (this.selectedAnnotation) {
1175
+ this.setROIStyle(this.selectedAnnotation.uid, {
1176
+ stroke: {
1177
+ color: '#00ff00'
1178
+ }
1179
+ });
1180
+ }
1181
+ this.selectedAnnotation = null;
1182
+ }
1183
+
1184
+ /**
1185
+ * Selects the given RoiAnnotation instance, publishing an ANNOTATION_SELECTED
1186
+ * event to notify all the subscribers
1187
+ *
1188
+ * @param {RoiAnnotation} roiAnnotation The instance to be selected
1189
+ */
1190
+ selectAnnotation(roiAnnotation) {
1191
+ if (this.selectedAnnotation) this.clearSelection();
1192
+ this.selectedAnnotation = roiAnnotation;
1193
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_SELECTED, roiAnnotation);
1194
+ this.setROIStyle(roiAnnotation.uid, utils_styles.active);
1195
+ }
1196
+
1197
+ /**
1198
+ * Toggles overview map
1199
+ *
1200
+ * @param viewportIndex The active viewport index
1201
+ * @returns {void}
1202
+ */
1203
+ toggleOverviewMap(viewportIndex) {
1204
+ const managedViewers = Array.from(this.managedViewers);
1205
+ const managedViewer = managedViewers.find(mv => mv.viewportIndex === viewportIndex);
1206
+ if (managedViewer) {
1207
+ managedViewer.toggleOverviewMap();
1208
+ }
1209
+ }
1210
+
1211
+ /**
1212
+ * Removes a RoiAnnotation instance from the managed annotations and reflects
1213
+ * its removal on all third-party viewers being managed
1214
+ *
1215
+ * @param {RoiAnnotation} roiAnnotation The instance to be removed
1216
+ */
1217
+ removeAnnotation(roiAnnotation) {
1218
+ const {
1219
+ uid,
1220
+ studyInstanceUID,
1221
+ seriesInstanceUID
1222
+ } = roiAnnotation;
1223
+ const filter = managedViewer => managedViewer.studyInstanceUID === studyInstanceUID && managedViewer.seriesInstanceUID === seriesInstanceUID;
1224
+ const managedViewers = Array.from(this.managedViewers).filter(filter);
1225
+ managedViewers.forEach(managedViewer => managedViewer.removeRoiGraphic(uid));
1226
+ if (this.annotations[uid]) {
1227
+ this.roiUids.delete(uid);
1228
+ this.annotations[uid].destroy();
1229
+ delete this.annotations[uid];
1230
+ this._broadcastEvent(MicroscopyService_EVENTS.ANNOTATION_REMOVED, roiAnnotation);
1231
+ }
1232
+ }
1233
+
1234
+ /**
1235
+ * Focus the given RoiAnnotation instance by changing the OpenLayers' Map view
1236
+ * state of the managed viewer with the given viewport index.
1237
+ * If the image for the given annotation is not yet loaded into the viewport,
1238
+ * it will set a pendingFocus flag to true in order to perform the focus when
1239
+ * the managed viewer instance is created.
1240
+ *
1241
+ * @param {RoiAnnotation} roiAnnotation RoiAnnotation instance to be focused
1242
+ * @param {Number} viewportIndex Index of the viewport to focus
1243
+ */
1244
+ focusAnnotation(roiAnnotation, viewportIndex) {
1245
+ const filter = mv => mv.viewportIndex === viewportIndex;
1246
+ const managedViewer = Array.from(this.managedViewers).find(filter);
1247
+ if (managedViewer) {
1248
+ managedViewer.setViewStateByExtent(roiAnnotation);
1249
+ } else {
1250
+ this.pendingFocus = true;
1251
+ }
1252
+ }
1253
+
1254
+ /**
1255
+ * Synchronize the ROI graphics for all the managed viewers that has the same
1256
+ * series UID of the given managed viewer
1257
+ *
1258
+ * @param {ViewerManager} baseManagedViewer Reference managed viewer
1259
+ */
1260
+ synchronizeViewers(baseManagedViewer) {
1261
+ const {
1262
+ studyInstanceUID,
1263
+ seriesInstanceUID
1264
+ } = baseManagedViewer;
1265
+ const managedViewers = this._getManagedViewersForSeries(studyInstanceUID, seriesInstanceUID);
1266
+
1267
+ // Prevent infinite loops arrising from updates.
1268
+ managedViewers.forEach(managedViewer => this._removeManagedViewerSubscriptions(managedViewer));
1269
+ managedViewers.forEach(managedViewer => {
1270
+ if (managedViewer === baseManagedViewer) {
1271
+ return;
1272
+ }
1273
+ const annotations = this.getAnnotationsForSeries(studyInstanceUID, seriesInstanceUID);
1274
+ managedViewer.clearRoiGraphics();
1275
+ annotations.forEach(roiAnnotation => {
1276
+ managedViewer.addRoiGraphic(roiAnnotation.roiGraphic);
1277
+ });
1278
+ });
1279
+ managedViewers.forEach(managedViewer => this._addManagedViewerSubscriptions(managedViewer));
1280
+ }
1281
+
1282
+ /**
1283
+ * Activates interactions across all the viewers being managed
1284
+ *
1285
+ * @param {Array} interactions interactions
1286
+ */
1287
+ activateInteractions(interactions) {
1288
+ this.managedViewers.forEach(mv => mv.activateInteractions(interactions));
1289
+ this.activeInteractions = interactions;
1290
+ }
1291
+
1292
+ /**
1293
+ * Triggers the relabelling process for the given RoiAnnotation instance, by
1294
+ * publishing the RELABEL event to notify the subscribers
1295
+ *
1296
+ * @param {RoiAnnotation} roiAnnotation The instance to be relabelled
1297
+ * @param {boolean} newAnnotation Whether the annotation is newly drawn (so it deletes on cancel).
1298
+ */
1299
+ triggerRelabel(roiAnnotation) {
1300
+ let newAnnotation = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1301
+ let onRelabel = arguments.length > 2 ? arguments[2] : undefined;
1302
+ if (!onRelabel) {
1303
+ onRelabel = _ref => {
1304
+ let {
1305
+ label
1306
+ } = _ref;
1307
+ return this.managedViewers.forEach(mv => mv.updateROIProperties({
1308
+ uid: roiAnnotation.uid,
1309
+ properties: {
1310
+ label
1311
+ }
1312
+ }));
1313
+ };
1314
+ }
1315
+ this._broadcastEvent(MicroscopyService_EVENTS.RELABEL, {
1316
+ roiAnnotation,
1317
+ deleteCallback: () => this.removeAnnotation(roiAnnotation),
1318
+ successCallback: onRelabel,
1319
+ newAnnotation
1320
+ });
1321
+ }
1322
+
1323
+ /**
1324
+ * Triggers the deletion process for the given RoiAnnotation instance, by
1325
+ * publishing the DELETE event to notify the subscribers
1326
+ *
1327
+ * @param {RoiAnnotation} roiAnnotation The instance to be deleted
1328
+ */
1329
+ triggerDelete(roiAnnotation) {
1330
+ this._broadcastEvent(MicroscopyService_EVENTS.DELETE, roiAnnotation);
1331
+ }
1332
+
1333
+ /**
1334
+ * Set ROI style for all managed viewers
1335
+ *
1336
+ * @param {string} uid The ROI uid that will be styled
1337
+ * @param {object} styleOptions - Style options
1338
+ * @param {object*} styleOptions.stroke - Style options for the outline of the geometry
1339
+ * @param {number[]} styleOptions.stroke.color - RGBA color of the outline
1340
+ * @param {number} styleOptions.stroke.width - Width of the outline
1341
+ * @param {object*} styleOptions.fill - Style options for body the geometry
1342
+ * @param {number[]} styleOptions.fill.color - RGBA color of the body
1343
+ * @param {object*} styleOptions.image - Style options for image
1344
+ */
1345
+ setROIStyle(uid, styleOptions) {
1346
+ this.managedViewers.forEach(mv => mv.setROIStyle(uid, styleOptions));
1347
+ }
1348
+ }
1349
+ MicroscopyService.REGISTRATION = serviceManager => {
1350
+ return {
1351
+ name: 'microscopyService',
1352
+ altName: 'MicroscopyService',
1353
+ create: _ref2 => {
1354
+ let {
1355
+ configuration = {}
1356
+ } = _ref2;
1357
+ return new MicroscopyService(serviceManager);
1358
+ }
1359
+ };
1360
+ };
1361
+
1362
+ // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
1363
+ var dcmjs_es = __webpack_require__(22737);
1364
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/callInputDialog.tsx
1365
+
1366
+
1367
+
1368
+ /**
1369
+ *
1370
+ * @param {*} data
1371
+ * @param {*} data.text
1372
+ * @param {*} data.label
1373
+ * @param {*} event
1374
+ * @param {func} callback
1375
+ * @param {*} isArrowAnnotateInputDialog
1376
+ */
1377
+ function callInputDialog(_ref) {
1378
+ let {
1379
+ uiDialogService,
1380
+ title = 'Enter your annotation',
1381
+ defaultValue = '',
1382
+ callback = (value, action) => {}
1383
+ } = _ref;
1384
+ const dialogId = 'microscopy-input-dialog';
1385
+ const onSubmitHandler = _ref2 => {
1386
+ let {
1387
+ action,
1388
+ value
1389
+ } = _ref2;
1390
+ switch (action.id) {
1391
+ case 'save':
1392
+ callback(value.value, action.id);
1393
+ break;
1394
+ case 'cancel':
1395
+ callback('', action.id);
1396
+ break;
1397
+ }
1398
+ uiDialogService.dismiss({
1399
+ id: dialogId
1400
+ });
1401
+ };
1402
+ if (uiDialogService) {
1403
+ uiDialogService.create({
1404
+ id: dialogId,
1405
+ centralize: true,
1406
+ isDraggable: false,
1407
+ showOverlay: true,
1408
+ content: src/* Dialog */.Vq,
1409
+ contentProps: {
1410
+ title: title,
1411
+ value: {
1412
+ value: defaultValue
1413
+ },
1414
+ noCloseButton: true,
1415
+ onClose: () => uiDialogService.dismiss({
1416
+ id: dialogId
1417
+ }),
1418
+ actions: [{
1419
+ id: 'cancel',
1420
+ text: 'Cancel',
1421
+ type: 'primary'
1422
+ }, {
1423
+ id: 'save',
1424
+ text: 'Save',
1425
+ type: 'secondary'
1426
+ }],
1427
+ onSubmit: onSubmitHandler,
1428
+ body: _ref3 => {
1429
+ let {
1430
+ value,
1431
+ setValue
1432
+ } = _ref3;
1433
+ return /*#__PURE__*/react.createElement("div", {
1434
+ className: "p-4 bg-primary-dark"
1435
+ }, /*#__PURE__*/react.createElement(src/* Input */.II, {
1436
+ autoFocus: true,
1437
+ className: "mt-2 bg-black border-primary-main",
1438
+ type: "text",
1439
+ containerClassName: "mr-2",
1440
+ value: value.defaultValue,
1441
+ onChange: event => {
1442
+ event.persist();
1443
+ setValue(value => ({
1444
+ ...value,
1445
+ value: event.target.value
1446
+ }));
1447
+ },
1448
+ onKeyPress: event => {
1449
+ if (event.key === 'Enter') {
1450
+ onSubmitHandler({
1451
+ value,
1452
+ action: {
1453
+ id: 'save'
1454
+ }
1455
+ });
1456
+ }
1457
+ }
1458
+ }));
1459
+ }
1460
+ }
1461
+ });
1462
+ }
1463
+ }
1464
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/DEVICE_OBSERVER_UID.js
1465
+ // We need to define a UID for this extension as a device, and it should be the same for all saves:
1466
+
1467
+ const uid = '2.25.285241207697168520771311899641885187923';
1468
+ /* harmony default export */ const DEVICE_OBSERVER_UID = (uid);
1469
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/constructSR.ts
1470
+
1471
+
1472
+
1473
+ /**
1474
+ *
1475
+ * @param {*} metadata - Microscopy Image instance metadata
1476
+ * @param {*} SeriesDescription - SR description
1477
+ * @param {*} annotations - Annotations
1478
+ *
1479
+ * @return Comprehensive3DSR dataset
1480
+ */
1481
+ function constructSR(metadata, _ref, annotations) {
1482
+ let {
1483
+ SeriesDescription,
1484
+ SeriesNumber
1485
+ } = _ref;
1486
+ // Handle malformed data
1487
+ if (!metadata.SpecimenDescriptionSequence) {
1488
+ metadata.SpecimenDescriptionSequence = {
1489
+ SpecimenUID: metadata.SeriesInstanceUID,
1490
+ SpecimenIdentifier: metadata.SeriesDescription
1491
+ };
1492
+ }
1493
+ const {
1494
+ SpecimenDescriptionSequence
1495
+ } = metadata;
1496
+
1497
+ // construct Comprehensive3DSR dataset
1498
+ const observationContext = new dcmjs_es["default"].sr.templates.ObservationContext({
1499
+ observerPersonContext: new dcmjs_es["default"].sr.templates.ObserverContext({
1500
+ observerType: new dcmjs_es["default"].sr.coding.CodedConcept({
1501
+ value: '121006',
1502
+ schemeDesignator: 'DCM',
1503
+ meaning: 'Person'
1504
+ }),
1505
+ observerIdentifyingAttributes: new dcmjs_es["default"].sr.templates.PersonObserverIdentifyingAttributes({
1506
+ name: '@ohif/extension-dicom-microscopy'
1507
+ })
1508
+ }),
1509
+ observerDeviceContext: new dcmjs_es["default"].sr.templates.ObserverContext({
1510
+ observerType: new dcmjs_es["default"].sr.coding.CodedConcept({
1511
+ value: '121007',
1512
+ schemeDesignator: 'DCM',
1513
+ meaning: 'Device'
1514
+ }),
1515
+ observerIdentifyingAttributes: new dcmjs_es["default"].sr.templates.DeviceObserverIdentifyingAttributes({
1516
+ uid: DEVICE_OBSERVER_UID
1517
+ })
1518
+ }),
1519
+ subjectContext: new dcmjs_es["default"].sr.templates.SubjectContext({
1520
+ subjectClass: new dcmjs_es["default"].sr.coding.CodedConcept({
1521
+ value: '121027',
1522
+ schemeDesignator: 'DCM',
1523
+ meaning: 'Specimen'
1524
+ }),
1525
+ subjectClassSpecificContext: new dcmjs_es["default"].sr.templates.SubjectContextSpecimen({
1526
+ uid: SpecimenDescriptionSequence.SpecimenUID,
1527
+ identifier: SpecimenDescriptionSequence.SpecimenIdentifier || metadata.SeriesInstanceUID,
1528
+ containerIdentifier: metadata.ContainerIdentifier || metadata.SeriesInstanceUID
1529
+ })
1530
+ })
1531
+ });
1532
+ const imagingMeasurements = [];
1533
+ for (let i = 0; i < annotations.length; i++) {
1534
+ const {
1535
+ roiGraphic: roi,
1536
+ label
1537
+ } = annotations[i];
1538
+ let {
1539
+ measurements,
1540
+ evaluations,
1541
+ marker,
1542
+ presentationState
1543
+ } = roi.properties;
1544
+ console.debug('[SR] storing marker...', marker);
1545
+ console.debug('[SR] storing measurements...', measurements);
1546
+ console.debug('[SR] storing evaluations...', evaluations);
1547
+ console.debug('[SR] storing presentation state...', presentationState);
1548
+ if (presentationState) presentationState.marker = marker;
1549
+
1550
+ /** Avoid incompatibility with dcmjs */
1551
+ measurements = measurements.map(measurement => {
1552
+ const ConceptName = Array.isArray(measurement.ConceptNameCodeSequence) ? measurement.ConceptNameCodeSequence[0] : measurement.ConceptNameCodeSequence;
1553
+ const MeasuredValue = Array.isArray(measurement.MeasuredValueSequence) ? measurement.MeasuredValueSequence[0] : measurement.MeasuredValueSequence;
1554
+ const MeasuredValueUnits = Array.isArray(MeasuredValue.MeasurementUnitsCodeSequence) ? MeasuredValue.MeasurementUnitsCodeSequence[0] : MeasuredValue.MeasurementUnitsCodeSequence;
1555
+ return new dcmjs_es["default"].sr.valueTypes.NumContentItem({
1556
+ name: new dcmjs_es["default"].sr.coding.CodedConcept({
1557
+ meaning: ConceptName.CodeMeaning,
1558
+ value: ConceptName.CodeValue,
1559
+ schemeDesignator: ConceptName.CodingSchemeDesignator
1560
+ }),
1561
+ value: MeasuredValue.NumericValue,
1562
+ unit: new dcmjs_es["default"].sr.coding.CodedConcept({
1563
+ value: MeasuredValueUnits.CodeValue,
1564
+ meaning: MeasuredValueUnits.CodeMeaning,
1565
+ schemeDesignator: MeasuredValueUnits.CodingSchemeDesignator
1566
+ })
1567
+ });
1568
+ });
1569
+
1570
+ /** Avoid incompatibility with dcmjs */
1571
+ evaluations = evaluations.map(evaluation => {
1572
+ const ConceptName = Array.isArray(evaluation.ConceptNameCodeSequence) ? evaluation.ConceptNameCodeSequence[0] : evaluation.ConceptNameCodeSequence;
1573
+ return new dcmjs_es["default"].sr.valueTypes.TextContentItem({
1574
+ name: new dcmjs_es["default"].sr.coding.CodedConcept({
1575
+ value: ConceptName.CodeValue,
1576
+ meaning: ConceptName.CodeMeaning,
1577
+ schemeDesignator: ConceptName.CodingSchemeDesignator
1578
+ }),
1579
+ value: evaluation.TextValue,
1580
+ relationshipType: evaluation.RelationshipType
1581
+ });
1582
+ });
1583
+ const identifier = `ROI #${i + 1}`;
1584
+ const group = new dcmjs_es["default"].sr.templates.PlanarROIMeasurementsAndQualitativeEvaluations({
1585
+ trackingIdentifier: new dcmjs_es["default"].sr.templates.TrackingIdentifier({
1586
+ uid: roi.uid,
1587
+ identifier: presentationState ? identifier.concat(`(${JSON.stringify(presentationState)})`) : identifier
1588
+ }),
1589
+ referencedRegion: new dcmjs_es["default"].sr.contentItems.ImageRegion3D({
1590
+ graphicType: roi.scoord3d.graphicType,
1591
+ graphicData: roi.scoord3d.graphicData,
1592
+ frameOfReferenceUID: roi.scoord3d.frameOfReferenceUID
1593
+ }),
1594
+ findingType: new dcmjs_es["default"].sr.coding.CodedConcept({
1595
+ value: label,
1596
+ schemeDesignator: '@ohif/extension-dicom-microscopy',
1597
+ meaning: 'FREETEXT'
1598
+ }),
1599
+ /** Evaluations will conflict with current tracking identifier */
1600
+ /** qualitativeEvaluations: evaluations, */
1601
+ measurements
1602
+ });
1603
+ imagingMeasurements.push(...group);
1604
+ }
1605
+ const measurementReport = new dcmjs_es["default"].sr.templates.MeasurementReport({
1606
+ languageOfContentItemAndDescendants: new dcmjs_es["default"].sr.templates.LanguageOfContentItemAndDescendants({}),
1607
+ observationContext,
1608
+ procedureReported: new dcmjs_es["default"].sr.coding.CodedConcept({
1609
+ value: '112703',
1610
+ schemeDesignator: 'DCM',
1611
+ meaning: 'Whole Slide Imaging'
1612
+ }),
1613
+ imagingMeasurements
1614
+ });
1615
+ const dataset = new dcmjs_es["default"].sr.documents.Comprehensive3DSR({
1616
+ content: measurementReport[0],
1617
+ evidence: [metadata],
1618
+ seriesInstanceUID: dcmjs_es["default"].data.DicomMetaDictionary.uid(),
1619
+ seriesNumber: SeriesNumber,
1620
+ seriesDescription: SeriesDescription || 'Whole slide imaging structured report',
1621
+ sopInstanceUID: dcmjs_es["default"].data.DicomMetaDictionary.uid(),
1622
+ instanceNumber: 1,
1623
+ manufacturer: 'dcmjs-org'
1624
+ });
1625
+ dataset.SpecificCharacterSet = 'ISO_IR 192';
1626
+ const fileMetaInformationVersionArray = new Uint8Array(2);
1627
+ fileMetaInformationVersionArray[1] = 1;
1628
+ dataset._meta = {
1629
+ FileMetaInformationVersion: {
1630
+ Value: [fileMetaInformationVersionArray.buffer],
1631
+ // TODO
1632
+ vr: 'OB'
1633
+ },
1634
+ MediaStorageSOPClassUID: dataset.sopClassUID,
1635
+ MediaStorageSOPInstanceUID: dataset.sopInstanceUID,
1636
+ TransferSyntaxUID: {
1637
+ Value: ['1.2.840.10008.1.2.1'],
1638
+ vr: 'UI'
1639
+ },
1640
+ ImplementationClassUID: {
1641
+ Value: [dcmjs_es["default"].data.DicomMetaDictionary.uid()],
1642
+ vr: 'UI'
1643
+ },
1644
+ ImplementationVersionName: {
1645
+ Value: ['@ohif/extension-dicom-microscopy'],
1646
+ vr: 'SH'
1647
+ }
1648
+ };
1649
+ return dataset;
1650
+ }
1651
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/saveByteArray.ts
1652
+ /**
1653
+ * Trigger file download from an array buffer
1654
+ * @param buffer
1655
+ * @param filename
1656
+ */
1657
+ function saveByteArray(buffer, filename) {
1658
+ var blob = new Blob([buffer], {
1659
+ type: 'application/dicom'
1660
+ });
1661
+ var link = document.createElement('a');
1662
+ link.href = window.URL.createObjectURL(blob);
1663
+ link.download = filename;
1664
+ link.click();
1665
+ }
1666
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx
1667
+
1668
+
1669
+
1670
+
1671
+
1672
+
1673
+
1674
+
1675
+
1676
+ let saving = false;
1677
+ const {
1678
+ datasetToBuffer
1679
+ } = dcmjs_es["default"].data;
1680
+ const formatArea = area => {
1681
+ let mult = 1;
1682
+ let unit = 'mm';
1683
+ if (area > 1000000) {
1684
+ unit = 'm';
1685
+ mult = 1 / 1000000;
1686
+ } else if (area < 1) {
1687
+ unit = 'μm';
1688
+ mult = 1000000;
1689
+ }
1690
+ return `${(area * mult).toFixed(2)} ${unit}²`;
1691
+ };
1692
+ const formatLength = (length, unit) => {
1693
+ let mult = 1;
1694
+ if (unit == 'km' || !unit && length > 1000000) {
1695
+ unit = 'km';
1696
+ mult = 1 / 1000000;
1697
+ } else if (unit == 'm' || !unit && length > 1000) {
1698
+ unit = 'm';
1699
+ mult = 1 / 1000;
1700
+ } else if (unit == 'μm' || !unit && length < 1) {
1701
+ unit = 'μm';
1702
+ mult = 1000;
1703
+ } else if (unit && unit != 'mm') {
1704
+ throw new Error(`Unknown length unit ${unit}`);
1705
+ } else {
1706
+ unit = 'mm';
1707
+ }
1708
+ return `${(length * mult).toFixed(2)} ${unit}`;
1709
+ };
1710
+ /**
1711
+ * Microscopy Measurements Panel Component
1712
+ *
1713
+ * @param props
1714
+ * @returns
1715
+ */
1716
+ function MicroscopyPanel(props) {
1717
+ const {
1718
+ microscopyService
1719
+ } = props.servicesManager.services;
1720
+ const [studyInstanceUID, setStudyInstanceUID] = (0,react.useState)(null);
1721
+ const [roiAnnotations, setRoiAnnotations] = (0,react.useState)([]);
1722
+ const [selectedAnnotation, setSelectedAnnotation] = (0,react.useState)(null);
1723
+ const {
1724
+ servicesManager,
1725
+ extensionManager
1726
+ } = props;
1727
+ const {
1728
+ uiDialogService,
1729
+ displaySetService
1730
+ } = servicesManager.services;
1731
+ (0,react.useEffect)(() => {
1732
+ const viewport = props.viewports[props.activeViewportIndex];
1733
+ if (viewport.displaySetInstanceUIDs[0]) {
1734
+ const displaySet = displaySetService.getDisplaySetByUID(viewport.displaySetInstanceUIDs[0]);
1735
+ if (displaySet) {
1736
+ setStudyInstanceUID(displaySet.StudyInstanceUID);
1737
+ }
1738
+ }
1739
+ }, [props.viewports, props.activeViewportIndex]);
1740
+ (0,react.useEffect)(() => {
1741
+ const onAnnotationUpdated = () => {
1742
+ const roiAnnotations = microscopyService.getAnnotationsForStudy(studyInstanceUID);
1743
+ setRoiAnnotations(roiAnnotations);
1744
+ };
1745
+ const onAnnotationSelected = () => {
1746
+ const selectedAnnotation = microscopyService.getSelectedAnnotation();
1747
+ setSelectedAnnotation(selectedAnnotation);
1748
+ };
1749
+ const onAnnotationRemoved = () => {
1750
+ onAnnotationUpdated();
1751
+ };
1752
+ const {
1753
+ unsubscribe: unsubscribeAnnotationUpdated
1754
+ } = microscopyService.subscribe(MicroscopyService_EVENTS.ANNOTATION_UPDATED, onAnnotationUpdated);
1755
+ const {
1756
+ unsubscribe: unsubscribeAnnotationSelected
1757
+ } = microscopyService.subscribe(MicroscopyService_EVENTS.ANNOTATION_SELECTED, onAnnotationSelected);
1758
+ const {
1759
+ unsubscribe: unsubscribeAnnotationRemoved
1760
+ } = microscopyService.subscribe(MicroscopyService_EVENTS.ANNOTATION_REMOVED, onAnnotationRemoved);
1761
+ onAnnotationUpdated();
1762
+ onAnnotationSelected();
1763
+
1764
+ // on unload unsubscribe from events
1765
+ return () => {
1766
+ unsubscribeAnnotationUpdated();
1767
+ unsubscribeAnnotationSelected();
1768
+ unsubscribeAnnotationRemoved();
1769
+ };
1770
+ }, [studyInstanceUID]);
1771
+
1772
+ /**
1773
+ * On clicking "Save Annotations" button, prompt an input modal for the
1774
+ * new series' description, and continue to save.
1775
+ *
1776
+ * @returns
1777
+ */
1778
+ const promptSave = () => {
1779
+ const annotations = microscopyService.getAnnotationsForStudy(studyInstanceUID);
1780
+ if (!annotations || saving) {
1781
+ return;
1782
+ }
1783
+ callInputDialog({
1784
+ uiDialogService,
1785
+ title: 'Enter description of the Series',
1786
+ defaultValue: '',
1787
+ callback: (value, action) => {
1788
+ switch (action) {
1789
+ case 'save':
1790
+ {
1791
+ saveFunction(value);
1792
+ }
1793
+ }
1794
+ }
1795
+ });
1796
+ };
1797
+ const getAllDisplaySets = studyMetadata => {
1798
+ let allDisplaySets = [];
1799
+ studyMetadata.series.forEach(series => {
1800
+ const displaySets = displaySetService.getDisplaySetsForSeries(series.SeriesInstanceUID);
1801
+ allDisplaySets = allDisplaySets.concat(displaySets);
1802
+ });
1803
+ return allDisplaySets;
1804
+ };
1805
+
1806
+ /**
1807
+ * Save annotations as a series
1808
+ *
1809
+ * @param SeriesDescription - series description
1810
+ * @returns
1811
+ */
1812
+ const saveFunction = async SeriesDescription => {
1813
+ const dataSource = extensionManager.getActiveDataSource()[0];
1814
+ const {
1815
+ onSaveComplete
1816
+ } = props;
1817
+ const annotations = microscopyService.getAnnotationsForStudy(studyInstanceUID);
1818
+ saving = true;
1819
+
1820
+ // There is only one viewer possible for one study,
1821
+ // Since once study contains multiple resolution levels (series) of one whole
1822
+ // Slide image.
1823
+
1824
+ const studyMetadata = core_src.DicomMetadataStore.getStudy(studyInstanceUID);
1825
+ const displaySets = getAllDisplaySets(studyMetadata);
1826
+ const smDisplaySet = displaySets.find(ds => ds.Modality === 'SM');
1827
+
1828
+ // Get the next available series number after 4700.
1829
+
1830
+ const dsWithMetadata = displaySets.filter(ds => ds.metadata && ds.metadata.SeriesNumber && typeof ds.metadata.SeriesNumber === 'number');
1831
+
1832
+ // Generate next series number
1833
+ const seriesNumbers = dsWithMetadata.map(ds => ds.metadata.SeriesNumber);
1834
+ const maxSeriesNumber = Math.max(...seriesNumbers, 4700);
1835
+ const SeriesNumber = maxSeriesNumber + 1;
1836
+ const {
1837
+ instance: metadata
1838
+ } = smDisplaySet;
1839
+
1840
+ // construct SR dataset
1841
+ const dataset = constructSR(metadata, {
1842
+ SeriesDescription,
1843
+ SeriesNumber
1844
+ }, annotations);
1845
+
1846
+ // Save in DICOM format
1847
+ try {
1848
+ if (dataSource) {
1849
+ if (dataSource.wadoRoot == 'saveDicom') {
1850
+ // download as DICOM file
1851
+ const part10Buffer = datasetToBuffer(dataset);
1852
+ saveByteArray(part10Buffer, `sr-microscopy.dcm`);
1853
+ } else {
1854
+ // Save into Web Data source
1855
+ const {
1856
+ StudyInstanceUID
1857
+ } = dataset;
1858
+ await dataSource.store.dicom(dataset);
1859
+ if (StudyInstanceUID) {
1860
+ dataSource.deleteStudyMetadataPromise(StudyInstanceUID);
1861
+ }
1862
+ }
1863
+ onSaveComplete({
1864
+ title: 'SR Saved',
1865
+ meassage: 'Measurements downloaded successfully',
1866
+ type: 'success'
1867
+ });
1868
+ } else {
1869
+ console.error('Server unspecified');
1870
+ }
1871
+ } catch (error) {
1872
+ onSaveComplete({
1873
+ title: 'SR Save Failed',
1874
+ message: error.message || error.toString(),
1875
+ type: 'error'
1876
+ });
1877
+ } finally {
1878
+ saving = false;
1879
+ }
1880
+ };
1881
+
1882
+ /**
1883
+ * On clicking "Reject annotations" button
1884
+ */
1885
+ const onDeleteCurrentSRHandler = async () => {
1886
+ try {
1887
+ const activeViewport = props.viewports[props.activeViewportIndex];
1888
+ const {
1889
+ StudyInstanceUID
1890
+ } = activeViewport;
1891
+
1892
+ // TODO: studies?
1893
+ const study = core_src.DicomMetadataStore.getStudy(StudyInstanceUID);
1894
+ const lastDerivedDisplaySet = study.derivedDisplaySets.sort((ds1, ds2) => {
1895
+ const dateTime1 = Number(`${ds1.SeriesDate}${ds1.SeriesTime}`);
1896
+ const dateTime2 = Number(`${ds2.SeriesDate}${ds2.SeriesTime}`);
1897
+ return dateTime1 > dateTime2;
1898
+ })[study.derivedDisplaySets.length - 1];
1899
+
1900
+ // TODO: use dataSource.reject.dicom()
1901
+ // await DICOMSR.rejectMeasurements(
1902
+ // study.wadoRoot,
1903
+ // lastDerivedDisplaySet.StudyInstanceUID,
1904
+ // lastDerivedDisplaySet.SeriesInstanceUID
1905
+ // );
1906
+ props.onRejectComplete({
1907
+ title: 'Report rejected',
1908
+ message: 'Latest report rejected successfully',
1909
+ type: 'success'
1910
+ });
1911
+ } catch (error) {
1912
+ props.onRejectComplete({
1913
+ title: 'Failed to reject report',
1914
+ message: error.message,
1915
+ type: 'error'
1916
+ });
1917
+ }
1918
+ };
1919
+
1920
+ /**
1921
+ * Handler for clicking event of an annotation item.
1922
+ *
1923
+ * @param param0
1924
+ */
1925
+ const onMeasurementItemClickHandler = _ref => {
1926
+ let {
1927
+ uid
1928
+ } = _ref;
1929
+ const roiAnnotation = microscopyService.getAnnotation(uid);
1930
+ microscopyService.selectAnnotation(roiAnnotation);
1931
+ microscopyService.focusAnnotation(roiAnnotation, props.activeViewportIndex);
1932
+ };
1933
+
1934
+ /**
1935
+ * Handler for "Edit" action of an annotation item
1936
+ * @param param0
1937
+ */
1938
+ const onMeasurementItemEditHandler = _ref2 => {
1939
+ let {
1940
+ uid,
1941
+ isActive
1942
+ } = _ref2;
1943
+ props.commandsManager.runCommand('setLabel', {
1944
+ uid
1945
+ }, 'MICROSCOPY');
1946
+ };
1947
+
1948
+ // Convert ROI annotations managed by microscopyService into our
1949
+ // own format for display
1950
+ const data = roiAnnotations.map((roiAnnotation, index) => {
1951
+ const label = roiAnnotation.getDetailedLabel();
1952
+ const area = roiAnnotation.getArea();
1953
+ const length = roiAnnotation.getLength();
1954
+ const shortAxisLength = roiAnnotation.roiGraphic.properties.shortAxisLength;
1955
+ const isSelected = selectedAnnotation === roiAnnotation;
1956
+
1957
+ // other events
1958
+ const {
1959
+ uid
1960
+ } = roiAnnotation;
1961
+
1962
+ // display text
1963
+ const displayText = [];
1964
+ if (area !== undefined) {
1965
+ displayText.push(formatArea(area));
1966
+ } else if (length !== undefined) {
1967
+ displayText.push(shortAxisLength ? `${formatLength(length, 'μm')} x ${formatLength(shortAxisLength, 'μm')}` : `${formatLength(length, 'μm')}`);
1968
+ }
1969
+
1970
+ // convert to measurementItem format compatible with <MeasurementTable /> component
1971
+ return {
1972
+ uid,
1973
+ index,
1974
+ label,
1975
+ isActive: isSelected,
1976
+ displayText,
1977
+ roiAnnotation
1978
+ };
1979
+ });
1980
+ const disabled = data.length === 0;
1981
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
1982
+ className: "overflow-x-hidden overflow-y-auto ohif-scrollbar",
1983
+ "data-cy": 'measurements-panel'
1984
+ }, /*#__PURE__*/react.createElement(src/* MeasurementTable */.wt, {
1985
+ title: "Measurements",
1986
+ servicesManager: props.servicesManager,
1987
+ data: data,
1988
+ onClick: onMeasurementItemClickHandler,
1989
+ onEdit: onMeasurementItemEditHandler
1990
+ })), /*#__PURE__*/react.createElement("div", {
1991
+ className: "flex justify-center p-4"
1992
+ }, /*#__PURE__*/react.createElement(src/* ButtonGroup */.hE, {
1993
+ color: "black",
1994
+ size: "inherit"
1995
+ })));
1996
+ }
1997
+ const connectedMicroscopyPanel = (0,es/* withTranslation */.Zh)(['MicroscopyTable', 'Common'])(MicroscopyPanel);
1998
+ /* harmony default export */ const MicroscopyPanel_MicroscopyPanel = (connectedMicroscopyPanel);
1999
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/getPanelModule.tsx
2000
+
2001
+
2002
+
2003
+
2004
+ // TODO:
2005
+ // - No loading UI exists yet
2006
+ // - cancel promises when component is destroyed
2007
+ // - show errors in UI for thumbnails if promise fails
2008
+
2009
+ function getPanelModule(_ref) {
2010
+ let {
2011
+ commandsManager,
2012
+ extensionManager,
2013
+ servicesManager
2014
+ } = _ref;
2015
+ const wrappedMeasurementPanel = () => {
2016
+ const [{
2017
+ activeViewportIndex,
2018
+ viewports
2019
+ }, viewportGridService] = (0,src/* useViewportGrid */.O_)();
2020
+ return /*#__PURE__*/react.createElement(MicroscopyPanel_MicroscopyPanel, {
2021
+ viewports: viewports,
2022
+ activeViewportIndex: activeViewportIndex,
2023
+ onSaveComplete: () => {},
2024
+ onRejectComplete: () => {},
2025
+ commandsManager: commandsManager,
2026
+ servicesManager: servicesManager,
2027
+ extensionManager: extensionManager
2028
+ });
2029
+ };
2030
+ return [{
2031
+ name: 'measure',
2032
+ iconName: 'tab-linear',
2033
+ iconLabel: 'Measure',
2034
+ label: 'Measurements',
2035
+ secondaryLabel: 'Measurements',
2036
+ component: wrappedMeasurementPanel
2037
+ }];
2038
+ }
2039
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/getCommandsModule.ts
2040
+
2041
+
2042
+ function getCommandsModule(_ref) {
2043
+ let {
2044
+ servicesManager,
2045
+ commandsManager,
2046
+ extensionManager
2047
+ } = _ref;
2048
+ const {
2049
+ viewportGridService,
2050
+ uiDialogService,
2051
+ microscopyService
2052
+ } = servicesManager.services;
2053
+ const actions = {
2054
+ // Measurement tool commands:
2055
+ deleteMeasurement: _ref2 => {
2056
+ let {
2057
+ uid
2058
+ } = _ref2;
2059
+ if (uid) {
2060
+ const roiAnnotation = microscopyService.getAnnotation(uid);
2061
+ if (roiAnnotation) microscopyService.removeAnnotation(roiAnnotation);
2062
+ }
2063
+ },
2064
+ setLabel: _ref3 => {
2065
+ let {
2066
+ uid
2067
+ } = _ref3;
2068
+ const roiAnnotation = microscopyService.getAnnotation(uid);
2069
+ callInputDialog({
2070
+ uiDialogService,
2071
+ title: 'Enter your annotation',
2072
+ defaultValue: '',
2073
+ callback: (value, action) => {
2074
+ switch (action) {
2075
+ case 'save':
2076
+ {
2077
+ roiAnnotation.setLabel(value);
2078
+ microscopyService.triggerRelabel(roiAnnotation);
2079
+ }
2080
+ }
2081
+ }
2082
+ });
2083
+ },
2084
+ setToolActive: _ref4 => {
2085
+ let {
2086
+ toolName,
2087
+ toolGroupId = 'MICROSCOPY'
2088
+ } = _ref4;
2089
+ const dragPanOnMiddle = ['dragPan', {
2090
+ bindings: {
2091
+ mouseButtons: ['middle']
2092
+ }
2093
+ }];
2094
+ const dragZoomOnRight = ['dragZoom', {
2095
+ bindings: {
2096
+ mouseButtons: ['right']
2097
+ }
2098
+ }];
2099
+ if (['line', 'box', 'circle', 'point', 'polygon', 'freehandpolygon', 'freehandline'].indexOf(toolName) >= 0) {
2100
+ // TODO: read from configuration
2101
+ let options = {
2102
+ geometryType: toolName,
2103
+ vertexEnabled: true,
2104
+ styleOptions: utils_styles["default"],
2105
+ bindings: {
2106
+ mouseButtons: ['left']
2107
+ }
2108
+ };
2109
+ if ('line' === toolName) {
2110
+ options.minPoints = 2;
2111
+ options.maxPoints = 2;
2112
+ } else if ('point' === toolName) {
2113
+ delete options.styleOptions;
2114
+ delete options.vertexEnabled;
2115
+ }
2116
+ microscopyService.activateInteractions([['draw', options], dragPanOnMiddle, dragZoomOnRight]);
2117
+ } else if (toolName == 'dragPan') {
2118
+ microscopyService.activateInteractions([['dragPan', {
2119
+ bindings: {
2120
+ mouseButtons: ['left', 'middle']
2121
+ }
2122
+ }], dragZoomOnRight]);
2123
+ } else {
2124
+ microscopyService.activateInteractions([[toolName, {
2125
+ bindings: {
2126
+ mouseButtons: ['left']
2127
+ }
2128
+ }], dragPanOnMiddle, dragZoomOnRight]);
2129
+ }
2130
+ },
2131
+ incrementActiveViewport: () => {
2132
+ const {
2133
+ activeViewportIndex,
2134
+ viewports
2135
+ } = viewportGridService.getState();
2136
+ const nextViewportIndex = (activeViewportIndex + 1) % viewports.length;
2137
+ viewportGridService.setActiveViewportIndex(nextViewportIndex);
2138
+ },
2139
+ decrementActiveViewport: () => {
2140
+ const {
2141
+ activeViewportIndex,
2142
+ viewports
2143
+ } = viewportGridService.getState();
2144
+ const nextViewportIndex = (activeViewportIndex - 1 + viewports.length) % viewports.length;
2145
+ viewportGridService.setActiveViewportIndex(nextViewportIndex);
2146
+ },
2147
+ toggleOverlays: () => {
2148
+ // overlay
2149
+ const overlays = document.getElementsByClassName('microscopy-viewport-overlay');
2150
+ let onoff = false; // true if this will toggle on
2151
+ for (let i = 0; i < overlays.length; i++) {
2152
+ if (i === 0) onoff = overlays.item(0).classList.contains('hidden');
2153
+ overlays.item(i).classList.toggle('hidden');
2154
+ }
2155
+
2156
+ // overview
2157
+ const {
2158
+ activeViewportIndex,
2159
+ viewports
2160
+ } = viewportGridService.getState();
2161
+ microscopyService.toggleOverviewMap(activeViewportIndex);
2162
+ },
2163
+ toggleAnnotations: () => {
2164
+ microscopyService.toggleROIsVisibility();
2165
+ }
2166
+ };
2167
+ const definitions = {
2168
+ deleteMeasurement: {
2169
+ commandFn: actions.deleteMeasurement,
2170
+ storeContexts: [],
2171
+ options: {}
2172
+ },
2173
+ setLabel: {
2174
+ commandFn: actions.setLabel,
2175
+ storeContexts: [],
2176
+ options: {}
2177
+ },
2178
+ setToolActive: {
2179
+ commandFn: actions.setToolActive,
2180
+ storeContexts: [],
2181
+ options: {}
2182
+ },
2183
+ incrementActiveViewport: {
2184
+ commandFn: actions.incrementActiveViewport,
2185
+ storeContexts: []
2186
+ },
2187
+ decrementActiveViewport: {
2188
+ commandFn: actions.decrementActiveViewport,
2189
+ storeContexts: []
2190
+ },
2191
+ toggleOverlays: {
2192
+ commandFn: actions.toggleOverlays,
2193
+ storeContexts: [],
2194
+ options: {}
2195
+ },
2196
+ toggleAnnotations: {
2197
+ commandFn: actions.toggleAnnotations,
2198
+ storeContexts: [],
2199
+ options: {}
2200
+ }
2201
+ };
2202
+ return {
2203
+ actions,
2204
+ definitions,
2205
+ defaultContext: 'MICROSCOPY'
2206
+ };
2207
+ }
2208
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js
2209
+
2210
+ const {
2211
+ utils
2212
+ } = core_src["default"];
2213
+ const SOP_CLASS_UIDS = {
2214
+ VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE: '1.2.840.10008.5.1.4.1.1.77.1.6'
2215
+ };
2216
+ const SOPClassHandlerId = '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopySopClassHandler';
2217
+ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
2218
+ // If the series has no instances, stop here
2219
+ if (!instances || !instances.length) {
2220
+ throw new Error('No instances were provided');
2221
+ }
2222
+ const instance = instances[0];
2223
+ let singleFrameInstance = instance;
2224
+ let currentFrames = +singleFrameInstance.NumberOfFrames || 1;
2225
+ for (const instanceI of instances) {
2226
+ const framesI = +instanceI.NumberOfFrames || 1;
2227
+ if (framesI < currentFrames) {
2228
+ singleFrameInstance = instanceI;
2229
+ currentFrames = framesI;
2230
+ }
2231
+ }
2232
+ let imageIdForThumbnail = null;
2233
+ if (singleFrameInstance) {
2234
+ if (currentFrames == 1) {
2235
+ // Not all DICOM server implementations support thumbnail service,
2236
+ // So if we have a single-frame image, we will prefer it.
2237
+ imageIdForThumbnail = singleFrameInstance.imageId;
2238
+ }
2239
+ if (!imageIdForThumbnail) {
2240
+ // use the thumbnail service provided by DICOM server
2241
+ const dataSource = extensionManager.getActiveDataSource()[0];
2242
+ imageIdForThumbnail = dataSource.getImageIdsForInstance({
2243
+ instance: singleFrameInstance,
2244
+ thumbnail: true
2245
+ });
2246
+ }
2247
+ }
2248
+ const {
2249
+ FrameOfReferenceUID,
2250
+ SeriesDescription,
2251
+ ContentDate,
2252
+ ContentTime,
2253
+ SeriesNumber,
2254
+ StudyInstanceUID,
2255
+ SeriesInstanceUID,
2256
+ SOPInstanceUID,
2257
+ SOPClassUID
2258
+ } = instance;
2259
+ instances = instances.map(inst => {
2260
+ // NOTE: According to DICOM standard a series should have a FrameOfReferenceUID
2261
+ // When the Microscopy file was built by certain tool from multiple image files,
2262
+ // each instance's FrameOfReferenceUID is sometimes different.
2263
+ // Even though this means the file was not well formatted DICOM VL Whole Slide Microscopy Image,
2264
+ // the case is so often, so let's override this value manually here.
2265
+ //
2266
+ // https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.4.html#sect_C.7.4.1.1.1
2267
+
2268
+ inst.FrameOfReferenceUID = instance.FrameOfReferenceUID;
2269
+ return inst;
2270
+ });
2271
+ const othersFrameOfReferenceUID = instances.filter(v => v).map(inst => inst.FrameOfReferenceUID).filter((value, index, array) => array.indexOf(value) === index);
2272
+ if (othersFrameOfReferenceUID.length > 1) {
2273
+ console.warn('Expected FrameOfReferenceUID of difference instances within a series to be the same, found multiple different values', othersFrameOfReferenceUID);
2274
+ }
2275
+ const displaySet = {
2276
+ plugin: 'microscopy',
2277
+ Modality: 'SM',
2278
+ altImageText: 'Microscopy',
2279
+ displaySetInstanceUID: utils.guid(),
2280
+ SOPInstanceUID,
2281
+ SeriesInstanceUID,
2282
+ StudyInstanceUID,
2283
+ FrameOfReferenceUID,
2284
+ SOPClassHandlerId,
2285
+ SOPClassUID,
2286
+ SeriesDescription: SeriesDescription || 'Microscopy Data',
2287
+ // Map ContentDate/Time to SeriesTime for series list sorting.
2288
+ SeriesDate: ContentDate,
2289
+ SeriesTime: ContentTime,
2290
+ SeriesNumber,
2291
+ firstInstance: singleFrameInstance,
2292
+ // top level instance in the image Pyramid
2293
+ instance,
2294
+ numImageFrames: 0,
2295
+ numInstances: 1,
2296
+ imageIdForThumbnail,
2297
+ // thumbnail image
2298
+ others: instances,
2299
+ // all other level instances in the image Pyramid
2300
+ othersFrameOfReferenceUID
2301
+ };
2302
+ return [displaySet];
2303
+ }
2304
+ function getDicomMicroscopySopClassHandler(_ref) {
2305
+ let {
2306
+ servicesManager,
2307
+ extensionManager
2308
+ } = _ref;
2309
+ const getDisplaySetsFromSeries = instances => {
2310
+ return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
2311
+ };
2312
+ return {
2313
+ name: 'DicomMicroscopySopClassHandler',
2314
+ sopClassUids: [SOP_CLASS_UIDS.VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE],
2315
+ getDisplaySetsFromSeries
2316
+ };
2317
+ }
2318
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/dcmCodeValues.js
2319
+ const DCM_CODE_VALUES = {
2320
+ IMAGING_MEASUREMENTS: '126010',
2321
+ MEASUREMENT_GROUP: '125007',
2322
+ IMAGE_REGION: '111030',
2323
+ FINDING: '121071',
2324
+ TRACKING_UNIQUE_IDENTIFIER: '112039',
2325
+ LENGTH: '410668003',
2326
+ AREA: '42798000',
2327
+ SHORT_AXIS: 'G-A186',
2328
+ LONG_AXIS: 'G-A185',
2329
+ ELLIPSE_AREA: 'G-D7FE' // TODO: Remove this
2330
+ };
2331
+
2332
+ /* harmony default export */ const dcmCodeValues = (DCM_CODE_VALUES);
2333
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/toArray.js
2334
+ function toArray(item) {
2335
+ return Array.isArray(item) ? item : [item];
2336
+ }
2337
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/loadSR.js
2338
+
2339
+
2340
+
2341
+ const MeasurementReport = dcmjs_es["default"].adapters.DICOMMicroscopyViewer.MeasurementReport;
2342
+
2343
+ // Define as async so that it returns a promise, expected by the ViewportGrid
2344
+ async function loadSR(microscopyService, microscopySRDisplaySet, referencedDisplaySet) {
2345
+ const naturalizedDataset = microscopySRDisplaySet.metadata;
2346
+ const {
2347
+ StudyInstanceUID,
2348
+ FrameOfReferenceUID
2349
+ } = referencedDisplaySet;
2350
+ const managedViewers = microscopyService.getManagedViewersForStudy(StudyInstanceUID);
2351
+ if (!managedViewers || !managedViewers.length) {
2352
+ return;
2353
+ }
2354
+ microscopySRDisplaySet.isLoaded = true;
2355
+ const {
2356
+ rois,
2357
+ labels
2358
+ } = await _getROIsFromToolState(naturalizedDataset, FrameOfReferenceUID);
2359
+ const managedViewer = managedViewers[0];
2360
+ for (let i = 0; i < rois.length; i++) {
2361
+ // NOTE: When saving Microscopy SR, we are attaching identifier property
2362
+ // to each ROI, and when read for display, it is coming in as "TEXT"
2363
+ // evaluation.
2364
+ // As the Dicom Microscopy Viewer will override styles for "Text" evalutations
2365
+ // to hide all other geometries, we are going to manually remove that
2366
+ // evaluation item.
2367
+ const roi = rois[i];
2368
+ const roiSymbols = Object.getOwnPropertySymbols(roi);
2369
+ const _properties = roiSymbols.find(s => s.description === 'properties');
2370
+ const properties = roi[_properties];
2371
+ properties['evaluations'] = [];
2372
+ managedViewer.addRoiGraphicWithLabel(roi, labels[i]);
2373
+ }
2374
+ }
2375
+ async function _getROIsFromToolState(naturalizedDataset, FrameOfReferenceUID) {
2376
+ const toolState = MeasurementReport.generateToolState(naturalizedDataset);
2377
+ const tools = Object.getOwnPropertyNames(toolState);
2378
+ const DICOMMicroscopyViewer = await __webpack_require__.e(/* import() | dicom-microscopy-viewer */ 18).then(__webpack_require__.t.bind(__webpack_require__, 78457, 23));
2379
+ const measurementGroupContentItems = _getMeasurementGroups(naturalizedDataset);
2380
+ const rois = [];
2381
+ const labels = [];
2382
+ tools.forEach(t => {
2383
+ const toolSpecificToolState = toolState[t];
2384
+ let scoord3d;
2385
+ const capsToolType = t.toUpperCase();
2386
+ const measurementGroupContentItemsForTool = measurementGroupContentItems.filter(mg => {
2387
+ const imageRegionContentItem = toArray(mg.ContentSequence).find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.IMAGE_REGION);
2388
+ return imageRegionContentItem.GraphicType === capsToolType;
2389
+ });
2390
+ toolSpecificToolState.forEach((coordinates, index) => {
2391
+ const properties = {};
2392
+ const options = {
2393
+ coordinates,
2394
+ frameOfReferenceUID: FrameOfReferenceUID
2395
+ };
2396
+ if (t === 'Polygon') {
2397
+ scoord3d = new DICOMMicroscopyViewer.scoord3d.Polygon(options);
2398
+ } else if (t === 'Polyline') {
2399
+ scoord3d = new DICOMMicroscopyViewer.scoord3d.Polyline(options);
2400
+ } else if (t === 'Point') {
2401
+ scoord3d = new DICOMMicroscopyViewer.scoord3d.Point(options);
2402
+ } else if (t === 'Ellipse') {
2403
+ scoord3d = new DICOMMicroscopyViewer.scoord3d.Ellipse(options);
2404
+ } else {
2405
+ throw new Error('Unsupported tool type');
2406
+ }
2407
+ const measurementGroup = measurementGroupContentItemsForTool[index];
2408
+ const findingGroup = toArray(measurementGroup.ContentSequence).find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.FINDING);
2409
+ const trackingGroup = toArray(measurementGroup.ContentSequence).find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.TRACKING_UNIQUE_IDENTIFIER);
2410
+
2411
+ /**
2412
+ * Extract presentation state from tracking identifier.
2413
+ * Currently is stored in SR but should be stored in its tags.
2414
+ */
2415
+ if (trackingGroup) {
2416
+ const regExp = /\(([^)]+)\)/;
2417
+ const matches = regExp.exec(trackingGroup.TextValue);
2418
+ if (matches && matches[1]) {
2419
+ properties.presentationState = JSON.parse(matches[1]);
2420
+ properties.marker = properties.presentationState.marker;
2421
+ }
2422
+ }
2423
+ let measurements = toArray(measurementGroup.ContentSequence).filter(ci => [dcmCodeValues.LENGTH, dcmCodeValues.AREA, dcmCodeValues.SHORT_AXIS, dcmCodeValues.LONG_AXIS, dcmCodeValues.ELLIPSE_AREA].includes(ci.ConceptNameCodeSequence.CodeValue));
2424
+ let evaluations = toArray(measurementGroup.ContentSequence).filter(ci => [dcmCodeValues.TRACKING_UNIQUE_IDENTIFIER].includes(ci.ConceptNameCodeSequence.CodeValue));
2425
+
2426
+ /**
2427
+ * TODO: Resolve bug in DCMJS.
2428
+ * ConceptNameCodeSequence should be a sequence with only one item.
2429
+ */
2430
+ evaluations = evaluations.map(evaluation => {
2431
+ const e = {
2432
+ ...evaluation
2433
+ };
2434
+ e.ConceptNameCodeSequence = toArray(e.ConceptNameCodeSequence);
2435
+ return e;
2436
+ });
2437
+
2438
+ /**
2439
+ * TODO: Resolve bug in DCMJS.
2440
+ * ConceptNameCodeSequence should be a sequence with only one item.
2441
+ */
2442
+ measurements = measurements.map(measurement => {
2443
+ const m = {
2444
+ ...measurement
2445
+ };
2446
+ m.ConceptNameCodeSequence = toArray(m.ConceptNameCodeSequence);
2447
+ return m;
2448
+ });
2449
+ if (measurements && measurements.length) {
2450
+ properties.measurements = measurements;
2451
+ console.debug('[SR] retrieving measurements...', measurements);
2452
+ }
2453
+ if (evaluations && evaluations.length) {
2454
+ properties.evaluations = evaluations;
2455
+ console.debug('[SR] retrieving evaluations...', evaluations);
2456
+ }
2457
+ const roi = new DICOMMicroscopyViewer.roi.ROI({
2458
+ scoord3d,
2459
+ properties
2460
+ });
2461
+ rois.push(roi);
2462
+ if (findingGroup) {
2463
+ labels.push(findingGroup.ConceptCodeSequence.CodeValue);
2464
+ } else {
2465
+ labels.push('');
2466
+ }
2467
+ });
2468
+ });
2469
+ return {
2470
+ rois,
2471
+ labels
2472
+ };
2473
+ }
2474
+ function _getMeasurementGroups(naturalizedDataset) {
2475
+ const {
2476
+ ContentSequence
2477
+ } = naturalizedDataset;
2478
+ const imagingMeasurementsContentItem = ContentSequence.find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.IMAGING_MEASUREMENTS);
2479
+ const measurementGroupContentItems = toArray(imagingMeasurementsContentItem.ContentSequence).filter(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.MEASUREMENT_GROUP);
2480
+ return measurementGroupContentItems;
2481
+ }
2482
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/utils/getSourceDisplaySet.js
2483
+ /**
2484
+ * Get referenced SM displaySet from SR displaySet
2485
+ *
2486
+ * @param {*} allDisplaySets
2487
+ * @param {*} microscopySRDisplaySet
2488
+ * @returns
2489
+ */
2490
+ function getSourceDisplaySet(allDisplaySets, microscopySRDisplaySet) {
2491
+ const {
2492
+ ReferencedFrameOfReferenceUID
2493
+ } = microscopySRDisplaySet;
2494
+ const otherDisplaySets = allDisplaySets.filter(ds => ds.displaySetInstanceUID !== microscopySRDisplaySet.displaySetInstanceUID);
2495
+ const referencedDisplaySet = otherDisplaySets.find(displaySet => displaySet.Modality === 'SM' && (displaySet.FrameOfReferenceUID === ReferencedFrameOfReferenceUID ||
2496
+ // sometimes each depth instance has the different FrameOfReferenceID
2497
+ displaySet.othersFrameOfReferenceUID.includes(ReferencedFrameOfReferenceUID)));
2498
+ if (!referencedDisplaySet && otherDisplaySets.length >= 1) {
2499
+ console.warn('No display set with FrameOfReferenceUID', ReferencedFrameOfReferenceUID, 'single series, assuming data error, defaulting to only series.');
2500
+ return otherDisplaySets.find(displaySet => displaySet.Modality === 'SM');
2501
+ }
2502
+ return referencedDisplaySet;
2503
+ }
2504
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/DicomMicroscopySRSopClassHandler.js
2505
+
2506
+
2507
+
2508
+
2509
+
2510
+ const {
2511
+ utils: DicomMicroscopySRSopClassHandler_utils
2512
+ } = core_src["default"];
2513
+ const DicomMicroscopySRSopClassHandler_SOP_CLASS_UIDS = {
2514
+ COMPREHENSIVE_3D_SR: '1.2.840.10008.5.1.4.1.1.88.34'
2515
+ };
2516
+ const DicomMicroscopySRSopClassHandler_SOPClassHandlerId = '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopySRSopClassHandler';
2517
+ function _getReferencedFrameOfReferenceUID(naturalizedDataset) {
2518
+ const {
2519
+ ContentSequence
2520
+ } = naturalizedDataset;
2521
+ const imagingMeasurementsContentItem = ContentSequence.find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.IMAGING_MEASUREMENTS);
2522
+ const firstMeasurementGroupContentItem = toArray(imagingMeasurementsContentItem.ContentSequence).find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.MEASUREMENT_GROUP);
2523
+ const imageRegionContentItem = toArray(firstMeasurementGroupContentItem.ContentSequence).find(ci => ci.ConceptNameCodeSequence.CodeValue === dcmCodeValues.IMAGE_REGION);
2524
+ return imageRegionContentItem.ReferencedFrameOfReferenceUID;
2525
+ }
2526
+ function DicomMicroscopySRSopClassHandler_getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
2527
+ // If the series has no instances, stop here
2528
+ if (!instances || !instances.length) {
2529
+ throw new Error('No instances were provided');
2530
+ }
2531
+ const {
2532
+ displaySetService,
2533
+ microscopyService
2534
+ } = servicesManager.services;
2535
+ const instance = instances[0];
2536
+
2537
+ // TODO ! Consumption of DICOMMicroscopySRSOPClassHandler to a derived dataset or normal dataset?
2538
+ // TOOD -> Easy to swap this to a "non-derived" displaySet, but unfortunately need to put it in a different extension.
2539
+ const naturalizedDataset = core_src.DicomMetadataStore.getSeries(instance.StudyInstanceUID, instance.SeriesInstanceUID).instances[0];
2540
+ const ReferencedFrameOfReferenceUID = _getReferencedFrameOfReferenceUID(naturalizedDataset);
2541
+ const {
2542
+ FrameOfReferenceUID,
2543
+ SeriesDescription,
2544
+ ContentDate,
2545
+ ContentTime,
2546
+ SeriesNumber,
2547
+ StudyInstanceUID,
2548
+ SeriesInstanceUID,
2549
+ SOPInstanceUID,
2550
+ SOPClassUID
2551
+ } = instance;
2552
+ const displaySet = {
2553
+ plugin: 'microscopy',
2554
+ Modality: 'SR',
2555
+ altImageText: 'Microscopy SR',
2556
+ displaySetInstanceUID: DicomMicroscopySRSopClassHandler_utils.guid(),
2557
+ SOPInstanceUID,
2558
+ SeriesInstanceUID,
2559
+ StudyInstanceUID,
2560
+ ReferencedFrameOfReferenceUID,
2561
+ SOPClassHandlerId: DicomMicroscopySRSopClassHandler_SOPClassHandlerId,
2562
+ SOPClassUID,
2563
+ SeriesDescription,
2564
+ // Map the content date/time to the series date/time, these are only used for filtering.
2565
+ SeriesDate: ContentDate,
2566
+ SeriesTime: ContentTime,
2567
+ SeriesNumber,
2568
+ instance,
2569
+ metadata: naturalizedDataset,
2570
+ isDerived: true,
2571
+ isLoading: false,
2572
+ isLoaded: false,
2573
+ loadError: false
2574
+ };
2575
+ displaySet.load = function (referencedDisplaySet) {
2576
+ return loadSR(microscopyService, displaySet, referencedDisplaySet).catch(error => {
2577
+ displaySet.isLoaded = false;
2578
+ displaySet.loadError = true;
2579
+ throw new Error(error);
2580
+ });
2581
+ };
2582
+ displaySet.getSourceDisplaySet = function () {
2583
+ let allDisplaySets = [];
2584
+ const studyMetadata = core_src.DicomMetadataStore.getStudy(StudyInstanceUID);
2585
+ studyMetadata.series.forEach(series => {
2586
+ const displaySets = displaySetService.getDisplaySetsForSeries(series.SeriesInstanceUID);
2587
+ allDisplaySets = allDisplaySets.concat(displaySets);
2588
+ });
2589
+ return getSourceDisplaySet(allDisplaySets, displaySet);
2590
+ };
2591
+ return [displaySet];
2592
+ }
2593
+ function getDicomMicroscopySRSopClassHandler(_ref) {
2594
+ let {
2595
+ servicesManager,
2596
+ extensionManager
2597
+ } = _ref;
2598
+ const getDisplaySetsFromSeries = instances => {
2599
+ return DicomMicroscopySRSopClassHandler_getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
2600
+ };
2601
+ return {
2602
+ name: 'DicomMicroscopySRSopClassHandler',
2603
+ sopClassUids: [DicomMicroscopySRSopClassHandler_SOP_CLASS_UIDS.COMPREHENSIVE_3D_SR],
2604
+ getDisplaySetsFromSeries
2605
+ };
2606
+ }
2607
+ ;// CONCATENATED MODULE: ../../../extensions/dicom-microscopy/src/index.tsx
2608
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2609
+
2610
+
2611
+
2612
+
2613
+
2614
+
2615
+
2616
+
2617
+ const Component = /*#__PURE__*/react.lazy(() => {
2618
+ return Promise.all(/* import() */[__webpack_require__.e(780), __webpack_require__.e(331), __webpack_require__.e(935), __webpack_require__.e(151), __webpack_require__.e(664), __webpack_require__.e(82), __webpack_require__.e(55)]).then(__webpack_require__.bind(__webpack_require__, 76440));
2619
+ });
2620
+ const MicroscopyViewport = props => {
2621
+ return /*#__PURE__*/react.createElement(react.Suspense, {
2622
+ fallback: /*#__PURE__*/react.createElement("div", null, "Loading...")
2623
+ }, /*#__PURE__*/react.createElement(Component, props));
2624
+ };
2625
+
2626
+ /**
2627
+ * You can remove any of the following modules if you don't need them.
2628
+ */
2629
+ /* harmony default export */ const dicom_microscopy_src = ({
2630
+ /**
2631
+ * Only required property. Should be a unique value across all extensions.
2632
+ * You ID can be anything you want, but it should be unique.
2633
+ */
2634
+ id: id,
2635
+ async preRegistration(_ref) {
2636
+ let {
2637
+ servicesManager,
2638
+ commandsManager,
2639
+ configuration = {},
2640
+ appConfig
2641
+ } = _ref;
2642
+ servicesManager.registerService(MicroscopyService.REGISTRATION(servicesManager));
2643
+ },
2644
+ /**
2645
+ * ViewportModule should provide a list of viewports that will be available in OHIF
2646
+ * for Modes to consume and use in the viewports. Each viewport is defined by
2647
+ * {name, component} object. Example of a viewport module is the CornerstoneViewport
2648
+ * that is provided by the Cornerstone extension in OHIF.
2649
+ */
2650
+ getViewportModule(_ref2) {
2651
+ let {
2652
+ servicesManager,
2653
+ extensionManager,
2654
+ commandsManager
2655
+ } = _ref2;
2656
+ /**
2657
+ *
2658
+ * @param props {*}
2659
+ * @param props.displaySets
2660
+ * @param props.viewportIndex
2661
+ * @param props.viewportLabel
2662
+ * @param props.dataSource
2663
+ * @param props.viewportOptions
2664
+ * @param props.displaySetOptions
2665
+ * @returns
2666
+ */
2667
+ const ExtendedMicroscopyViewport = props => {
2668
+ const {
2669
+ viewportOptions
2670
+ } = props;
2671
+ const [viewportGrid, viewportGridService] = (0,src/* useViewportGrid */.O_)();
2672
+ const {
2673
+ viewports,
2674
+ activeViewportIndex
2675
+ } = viewportGrid;
2676
+ return /*#__PURE__*/react.createElement(MicroscopyViewport, _extends({
2677
+ servicesManager: servicesManager,
2678
+ extensionManager: extensionManager,
2679
+ commandsManager: commandsManager,
2680
+ activeViewportIndex: activeViewportIndex,
2681
+ setViewportActive: viewportIndex => {
2682
+ viewportGridService.setActiveViewportIndex(viewportIndex);
2683
+ },
2684
+ viewportData: viewportOptions
2685
+ }, props));
2686
+ };
2687
+ return [{
2688
+ name: 'microscopy-dicom',
2689
+ component: ExtendedMicroscopyViewport
2690
+ }];
2691
+ },
2692
+ /**
2693
+ * SopClassHandlerModule should provide a list of sop class handlers that will be
2694
+ * available in OHIF for Modes to consume and use to create displaySets from Series.
2695
+ * Each sop class handler is defined by a { name, sopClassUids, getDisplaySetsFromSeries}.
2696
+ * Examples include the default sop class handler provided by the default extension
2697
+ */
2698
+ getSopClassHandlerModule(_ref3) {
2699
+ let {
2700
+ servicesManager,
2701
+ commandsManager,
2702
+ extensionManager
2703
+ } = _ref3;
2704
+ return [getDicomMicroscopySopClassHandler({
2705
+ servicesManager,
2706
+ extensionManager
2707
+ }), getDicomMicroscopySRSopClassHandler({
2708
+ servicesManager,
2709
+ extensionManager
2710
+ })];
2711
+ },
2712
+ getPanelModule: getPanelModule,
2713
+ getCommandsModule: getCommandsModule
2714
+ });
2715
+
2716
+ /***/ }),
2717
+
2718
+ /***/ 78753:
2719
+ /***/ (() => {
2720
+
2721
+ /* (ignored) */
2722
+
2723
+ /***/ })
2724
+
2725
+ }]);