@ohif/app 3.7.0-beta.11 → 3.7.0-beta.110

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 (66) hide show
  1. package/dist/{917.bundle.b9983325adf849bff6fd.js → 12.bundle.5ea15cb6633d8028e47d.js} +6 -6
  2. package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.253395f320b72180da63.js} +6 -6
  3. package/dist/{351.bundle.0742237651aef9694a65.js → 181.bundle.ceb057236403bcb630ac.js} +226 -204
  4. package/dist/{351.css → 181.css} +1 -1
  5. package/dist/{744.bundle.4c4b884f90eb70482821.js → 19.bundle.03f809886c36c388d05c.js} +240 -381
  6. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.d3490836f71e001dd30f.js} +2089 -692
  7. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  8. package/dist/221.bundle.dc6dac346d724d6baeae.js +1779 -0
  9. package/dist/221.css +2 -0
  10. package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
  11. package/dist/{976.bundle.9cc2382162214ea0af2b.js → 236.bundle.7b906cd27864d65f32c0.js} +89 -105
  12. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.8084960e3318cda37317.js} +52 -36
  13. package/dist/{973.bundle.4584df05b320b94cace5.js → 281.bundle.c9854cc25c839e49c2c8.js} +18 -14
  14. package/dist/{82.bundle.9a0e7f08d4bce18d302f.js → 342.bundle.7d6c1e6bda1c67d729a7.js} +1802 -489
  15. package/dist/{404.bundle.3d65ff813eead20462d3.js → 359.bundle.72d017719489ff11057b.js} +47 -134
  16. package/dist/{192.bundle.950e5380ea63c6d635d5.js → 370.bundle.e55d75ff1bdccee16cde.js} +117 -103
  17. package/dist/{790.bundle.7327fec7833ceea2784b.js → 410.bundle.5b41c68cb0f210a83f13.js} +11 -9
  18. package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.af0a207c29b109f84159.js} +49 -17
  19. package/dist/{569.bundle.c8e771a8d28e237b32be.js → 451.bundle.9fd36f52ff69594f0669.js} +86 -106
  20. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.b3d77b83b1593c09a504.js} +78 -99
  21. package/dist/{199.bundle.f50ffd85a4334de2f5c1.js → 506.bundle.468dc6db2a9bfa96bb44.js} +11 -9
  22. package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 530.bundle.a03b6f942ace3e1baa1e.js} +726 -447
  23. package/dist/579.css +1 -0
  24. package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
  25. package/dist/613.bundle.74d59dec1983ef3efcdc.js +532 -0
  26. package/dist/{984.bundle.119557c018e6371b4628.js → 663.bundle.a6196baf5853e7d3f7c7.js} +68 -38
  27. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
  28. package/dist/{50.bundle.65ff60818862eda39d12.js → 687.bundle.e2c9c42ad3989a14d513.js} +218 -9
  29. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 743.bundle.489f7df3a089d4d374e1.js} +26294 -21326
  30. package/dist/757.bundle.ec8301d8e70d2b990f65.js +17067 -0
  31. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.4b2dc46a35012b898e1a.js} +95 -64
  32. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  33. package/dist/{283.bundle.1015e87c3a47b1f1379c.js → 788.bundle.f4493409508bdffa7af8.js} +120 -370
  34. package/dist/{642.bundle.8905e515ce593e57ceb1.js → 814.bundle.10a2cbf02b044387e68b.js} +6 -6
  35. package/dist/{707.bundle.f774f3e4a687ddd60a32.js → 82.bundle.9c6461625afd2e38b997.js} +1203 -804
  36. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.891f2e57b1b7bc2f4cb4.js} +81 -34
  37. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.4b3a7f2079d085fdbcb3.js} +34 -29
  38. package/dist/945.min.worker.js +1 -1
  39. package/dist/945.min.worker.js.map +1 -1
  40. package/dist/{270.bundle.4564621556b0f963a004.js → 957.bundle.9ea4506963ef8b2d84ba.js} +7095 -979
  41. package/dist/{208.bundle.7f610a302dc54c4924da.js → 99.bundle.334c4bd4e4e81aaf45ad.js} +86 -105
  42. package/dist/_redirects +1 -1
  43. package/dist/app-config.js +35 -17
  44. package/dist/app.bundle.css +13 -12
  45. package/dist/{app.bundle.3d598a4738bdc22950d3.js → app.bundle.fd6ac18b8874825722a0.js} +72771 -67313
  46. package/dist/assets/yandex-browser-manifest.json +1 -1
  47. package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
  48. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
  49. package/dist/{dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js → dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js} +5 -4
  50. package/dist/es6-shim.min.js +3569 -2
  51. package/dist/google.js +8 -7
  52. package/dist/index.html +2 -1
  53. package/dist/{index.worker.1c69152d710fa7b84bce.worker.js → index.worker.e62ecca63f1a2e124230.worker.js} +2 -2
  54. package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +1 -0
  55. package/dist/init-service-worker.js +3 -5
  56. package/dist/oidc-client.min.js +10857 -39
  57. package/dist/polyfill.min.js +184 -1
  58. package/dist/silent-refresh.html +18 -9
  59. package/dist/sw.js +1 -1
  60. package/package.json +21 -22
  61. package/dist/616.bundle.de530ae226dfa5573f6e.js +0 -685
  62. package/dist/780.bundle.fd0f13dc92e9caa0581e.js +0 -4769
  63. package/dist/index.worker.1c69152d710fa7b84bce.worker.js.map +0 -1
  64. /package/dist/{806.css → 19.css} +0 -0
  65. /package/dist/{55.css → 250.css} +0 -0
  66. /package/dist/{707.css → 82.css} +0 -0
@@ -1,6 +1,6 @@
1
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[707],{
1
+ (self["webpackChunk"] = self["webpackChunk"] || []).push([[82],{
2
2
 
3
- /***/ 31197:
3
+ /***/ 78227:
4
4
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5
5
 
6
6
  "use strict";
@@ -9,44 +9,44 @@ __webpack_require__.r(__webpack_exports__);
9
9
 
10
10
  // EXPORTS
11
11
  __webpack_require__.d(__webpack_exports__, {
12
- CornerstoneExtensionTypes: () => (/* reexport */ types_namespaceObject),
12
+ Types: () => (/* reexport */ types_namespaceObject),
13
13
  "default": () => (/* binding */ cornerstone_src),
14
14
  getActiveViewportEnabledElement: () => (/* reexport */ getActiveViewportEnabledElement),
15
15
  measurementMappingUtils: () => (/* reexport */ utils_namespaceObject),
16
16
  toolNames: () => (/* reexport */ toolNames)
17
17
  });
18
18
 
19
- // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/types/index.ts
20
- var types_namespaceObject = {};
21
- __webpack_require__.r(types_namespaceObject);
22
-
23
19
  // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/index.ts
24
20
  var utils_namespaceObject = {};
25
21
  __webpack_require__.r(utils_namespaceObject);
26
22
  __webpack_require__.d(utils_namespaceObject, {
23
+ getDisplayUnit: () => (utils_getDisplayUnit),
27
24
  getFirstAnnotationSelected: () => (getFirstAnnotationSelected),
28
25
  getHandlesFromPoints: () => (getHandlesFromPoints),
29
- getModalityUnit: () => (utils_getModalityUnit),
30
26
  getSOPInstanceAttributes: () => (getSOPInstanceAttributes/* default */.Z),
31
27
  isAnnotationSelected: () => (isAnnotationSelected),
32
28
  setAnnotationSelected: () => (setAnnotationSelected)
33
29
  });
34
30
 
31
+ // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/types/index.ts
32
+ var types_namespaceObject = {};
33
+ __webpack_require__.r(types_namespaceObject);
34
+
35
35
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
36
- var react = __webpack_require__(32735);
37
- // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 335 modules
38
- var esm = __webpack_require__(77331);
39
- // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 321 modules
40
- var dist_esm = __webpack_require__(57270);
41
- // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
42
- var src = __webpack_require__(48501);
36
+ var react = __webpack_require__(43001);
37
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 331 modules
38
+ var esm = __webpack_require__(3743);
39
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 348 modules
40
+ var dist_esm = __webpack_require__(14957);
41
+ // EXTERNAL MODULE: ../../core/src/index.ts + 65 modules
42
+ var src = __webpack_require__(71771);
43
43
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/streaming-image-volume-loader/dist/esm/index.js + 13 modules
44
- var streaming_image_volume_loader_dist_esm = __webpack_require__(28909);
44
+ var streaming_image_volume_loader_dist_esm = __webpack_require__(7087);
45
45
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/dicom-image-loader/dist/dynamic-import/cornerstoneDICOMImageLoader.min.js
46
- var cornerstoneDICOMImageLoader_min = __webpack_require__(99605);
46
+ var cornerstoneDICOMImageLoader_min = __webpack_require__(61539);
47
47
  var cornerstoneDICOMImageLoader_min_default = /*#__PURE__*/__webpack_require__.n(cornerstoneDICOMImageLoader_min);
48
48
  // EXTERNAL MODULE: ../../../node_modules/dicom-parser/dist/dicomParser.min.js
49
- var dicomParser_min = __webpack_require__(43264);
49
+ var dicomParser_min = __webpack_require__(56660);
50
50
  var dicomParser_min_default = /*#__PURE__*/__webpack_require__.n(dicomParser_min);
51
51
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/initWADOImageLoader.js
52
52
 
@@ -76,7 +76,7 @@ function initWebWorkers(appConfig) {
76
76
  initialized = true;
77
77
  }
78
78
  }
79
- function initWADOImageLoader(userAuthenticationService, appConfig) {
79
+ function initWADOImageLoader(userAuthenticationService, appConfig, extensionManager) {
80
80
  (cornerstoneDICOMImageLoader_min_default()).external.cornerstone = esm;
81
81
  (cornerstoneDICOMImageLoader_min_default()).external.dicomParser = (dicomParser_min_default());
82
82
  registerVolumeLoader('cornerstoneStreamingImageVolume', streaming_image_volume_loader_dist_esm/* cornerstoneStreamingImageVolumeLoader */.IU);
@@ -87,21 +87,17 @@ function initWADOImageLoader(userAuthenticationService, appConfig) {
87
87
  // will convert everything to integers (to be able to work with cornerstone-2d).
88
88
  // Until the default is set to true (which is the case for cornerstone3D),
89
89
  // we should set this flag to false.
90
- convertFloatPixelDataToInt: false
90
+ convertFloatPixelDataToInt: false,
91
+ use16BitDataType: Boolean(appConfig.use16BitDataType)
91
92
  },
92
93
  beforeSend: function (xhr) {
94
+ //TODO should be removed in the future and request emitted by DicomWebDataSource
95
+ const sourceConfig = extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
93
96
  const headers = userAuthenticationService.getAuthorizationHeader();
94
-
95
- // Request:
96
- // JPEG-LS Lossless (1.2.840.10008.1.2.4.80) if available, otherwise accept
97
- // whatever transfer-syntax the origin server provides.
98
- // For now we use image/jls and image/x-jls because some servers still use the old type
99
- // http://dicom.nema.org/medical/dicom/current/output/html/part18.html
97
+ const acceptHeader = src.utils.generateAcceptHeader(sourceConfig.acceptHeader, sourceConfig.requestTransferSyntaxUID, sourceConfig.omitQuotationForMultipartRequest);
100
98
  const xhrRequestHeaders = {
101
- Accept: appConfig.omitQuotationForMultipartRequest ? 'multipart/related; type=application/octet-stream' : 'multipart/related; type="application/octet-stream"'
102
- // 'multipart/related; type="image/x-jls", multipart/related; type="image/jls"; transfer-syntax="1.2.840.10008.1.2.4.80", multipart/related; type="image/x-jls", multipart/related; type="application/octet-stream"; transfer-syntax=*',
99
+ Accept: acceptHeader
103
100
  };
104
-
105
101
  if (headers) {
106
102
  Object.assign(xhrRequestHeaders, headers);
107
103
  }
@@ -116,14 +112,14 @@ function initWADOImageLoader(userAuthenticationService, appConfig) {
116
112
  function destroy() {
117
113
  // Note: we don't want to call .terminate on the webWorkerManager since
118
114
  // that resets the config
119
- const webWorkers = cornerstoneDICOMImageLoader_min.webWorkerManager.webWorkers;
115
+ const webWorkers = webWorkerManager.webWorkers;
120
116
  for (let i = 0; i < webWorkers.length; i++) {
121
117
  webWorkers[i].worker.terminate();
122
118
  }
123
119
  webWorkers.length = 0;
124
120
  }
125
- // EXTERNAL MODULE: ../../ui/src/index.js + 452 modules
126
- var ui_src = __webpack_require__(28619);
121
+ // EXTERNAL MODULE: ../../ui/src/index.js + 485 modules
122
+ var ui_src = __webpack_require__(71783);
127
123
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/callInputDialog.tsx
128
124
 
129
125
 
@@ -157,7 +153,9 @@ function callInputDialog(uiDialogService, data, callback) {
157
153
  } = _ref;
158
154
  switch (action.id) {
159
155
  case 'save':
160
- if (typeof validateFunc === 'function' && !validateFunc(value.label)) return;
156
+ if (typeof validateFunc === 'function' && !validateFunc(value.label)) {
157
+ return;
158
+ }
161
159
  callback(value.label, action.id);
162
160
  break;
163
161
  case 'cancel':
@@ -187,11 +185,11 @@ function callInputDialog(uiDialogService, data, callback) {
187
185
  actions: [{
188
186
  id: 'cancel',
189
187
  text: 'Cancel',
190
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
188
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
191
189
  }, {
192
190
  id: 'save',
193
191
  text: 'Save',
194
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
192
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
195
193
  }],
196
194
  onSubmit: onSubmitHandler,
197
195
  body: _ref2 => {
@@ -201,7 +199,7 @@ function callInputDialog(uiDialogService, data, callback) {
201
199
  } = _ref2;
202
200
  return /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
203
201
  autoFocus: true,
204
- className: "bg-black border-primary-main",
202
+ className: "border-primary-main bg-black",
205
203
  type: "text",
206
204
  id: "annotation",
207
205
  label: inputLabel,
@@ -232,17 +230,17 @@ function callInputDialog(uiDialogService, data, callback) {
232
230
  }
233
231
  /* harmony default export */ const utils_callInputDialog = (callInputDialog);
234
232
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/state.ts
235
- var state = __webpack_require__(21922);
233
+ var state = __webpack_require__(73704);
236
234
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/getActiveViewportEnabledElement.ts
237
235
 
238
236
 
239
237
  function getActiveViewportEnabledElement(viewportGridService) {
240
238
  const {
241
- activeViewportIndex
239
+ activeViewportId
242
240
  } = viewportGridService.getState();
243
241
  const {
244
242
  element
245
- } = (0,state/* getEnabledElement */.K8)(activeViewportIndex) || {};
243
+ } = (0,state/* getEnabledElement */.K8)(activeViewportId) || {};
246
244
  const enabledElement = (0,esm.getEnabledElement)(element);
247
245
  return enabledElement;
248
246
  }
@@ -322,11 +320,12 @@ function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
322
320
  const currentColumnPixelSpacing = calibratedPixelSpacing?.[1] || imagePlaneModule?.columnPixelSpacing || 1;
323
321
  const adjustCalibration = newLength => {
324
322
  const spacingScale = newLength / length;
325
- const rowSpacing = spacingScale * currentRowPixelSpacing;
326
- const colSpacing = spacingScale * currentColumnPixelSpacing;
327
323
 
328
324
  // trigger resize of the viewport to adjust the world/pixel mapping
329
- calibrateImageSpacing(imageId, viewport.getRenderingEngine(), rowSpacing, colSpacing);
325
+ calibrateImageSpacing(imageId, viewport.getRenderingEngine(), {
326
+ type: 'User',
327
+ scale: 1 / spacingScale
328
+ });
330
329
  };
331
330
  return new Promise((resolve, reject) => {
332
331
  if (!uiDialogService) {
@@ -358,9 +357,223 @@ function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
358
357
  });
359
358
  });
360
359
  }
360
+ // EXTERNAL MODULE: ../../core/src/utils/index.js + 25 modules
361
+ var utils = __webpack_require__(77250);
362
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx
363
+
364
+
365
+
366
+
367
+ /**
368
+ * Image Overlay Viewer tool is not a traditional tool that requires user interactin.
369
+ * But it is used to display Pixel Overlays. And it will provide toggling capability.
370
+ *
371
+ * The documentation for Overlay Plane Module of DICOM can be found in [C.9.2 of
372
+ * Part-3 of DICOM standard](https://dicom.nema.org/medical/dicom/2018b/output/chtml/part03/sect_C.9.2.html)
373
+ *
374
+ * Image Overlay rendered by this tool can be toggled on and off using
375
+ * toolGroup.setToolEnabled() and toolGroup.setToolDisabled()
376
+ */
377
+ class ImageOverlayViewerTool extends dist_esm.AnnotationDisplayTool {
378
+ constructor() {
379
+ let toolProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
380
+ let defaultToolProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
381
+ supportedInteractionTypes: [],
382
+ configuration: {
383
+ fillColor: [255, 127, 127, 255]
384
+ }
385
+ };
386
+ super(toolProps, defaultToolProps);
387
+ this._cachedOverlayMetadata = new Map();
388
+ this._cachedStats = {};
389
+ this.onSetToolDisabled = () => {
390
+ this._cachedStats = {};
391
+ this._cachedOverlayMetadata = new Map();
392
+ };
393
+ this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
394
+ const {
395
+ viewport
396
+ } = enabledElement;
397
+ const imageId = this.getReferencedImageId(viewport);
398
+ if (!imageId) {
399
+ return;
400
+ }
401
+ const overlays = this._cachedOverlayMetadata.get(imageId) ?? esm.metaData.get('overlayPlaneModule', imageId)?.overlays;
402
+
403
+ // no overlays
404
+ if (!overlays?.length) {
405
+ return;
406
+ }
407
+ this._cachedOverlayMetadata.set(imageId, overlays);
408
+ this._getCachedStat(imageId, overlays, this.configuration.fillColor).then(cachedStat => {
409
+ cachedStat.overlays.forEach(overlay => {
410
+ this._renderOverlay(enabledElement, svgDrawingHelper, overlay);
411
+ });
412
+ });
413
+ return true;
414
+ };
415
+ }
416
+ getReferencedImageId(viewport) {
417
+ if (viewport instanceof esm.VolumeViewport) {
418
+ return;
419
+ }
420
+ const targetId = this.getTargetId(viewport);
421
+ return targetId.split('imageId:')[1];
422
+ }
423
+ /**
424
+ * Render to DOM
425
+ *
426
+ * @param enabledElement
427
+ * @param svgDrawingHelper
428
+ * @param overlayData
429
+ * @returns
430
+ */
431
+ _renderOverlay(enabledElement, svgDrawingHelper, overlayData) {
432
+ const {
433
+ viewport
434
+ } = enabledElement;
435
+ const imageId = this.getReferencedImageId(viewport);
436
+ if (!imageId) {
437
+ return;
438
+ }
439
+
440
+ // Decide the rendering position of the overlay image on the current canvas
441
+ const {
442
+ _id,
443
+ columns: width,
444
+ rows: height,
445
+ x,
446
+ y
447
+ } = overlayData;
448
+ const overlayTopLeftWorldPos = esm.utilities.imageToWorldCoords(imageId, [x - 1,
449
+ // Remind that top-left corner's (x, y) is be (1, 1)
450
+ y - 1]);
451
+ const overlayTopLeftOnCanvas = viewport.worldToCanvas(overlayTopLeftWorldPos);
452
+ const overlayBottomRightWorldPos = esm.utilities.imageToWorldCoords(imageId, [width, height]);
453
+ const overlayBottomRightOnCanvas = viewport.worldToCanvas(overlayBottomRightWorldPos);
454
+
455
+ // add image to the annotations svg layer
456
+ const svgns = 'http://www.w3.org/2000/svg';
457
+ const svgNodeHash = `image-overlay-${_id}`;
458
+ const existingImageElement = svgDrawingHelper.getSvgNode(svgNodeHash);
459
+ const attributes = {
460
+ 'data-id': svgNodeHash,
461
+ width: overlayBottomRightOnCanvas[0] - overlayTopLeftOnCanvas[0],
462
+ height: overlayBottomRightOnCanvas[1] - overlayTopLeftOnCanvas[1],
463
+ x: overlayTopLeftOnCanvas[0],
464
+ y: overlayTopLeftOnCanvas[1],
465
+ href: overlayData.dataUrl
466
+ };
467
+ if (isNaN(attributes.x) || isNaN(attributes.y) || isNaN(attributes.width) || isNaN(attributes.height)) {
468
+ console.warn('Invalid rendering attribute for image overlay', attributes['data-id']);
469
+ return false;
470
+ }
471
+ if (existingImageElement) {
472
+ dist_esm.drawing.setAttributesIfNecessary(attributes, existingImageElement);
473
+ svgDrawingHelper.setNodeTouched(svgNodeHash);
474
+ } else {
475
+ const newImageElement = document.createElementNS(svgns, 'image');
476
+ dist_esm.drawing.setNewAttributesIfValid(attributes, newImageElement);
477
+ svgDrawingHelper.appendNode(newImageElement, svgNodeHash);
478
+ }
479
+ return true;
480
+ }
481
+ async _getCachedStat(imageId, overlayMetadata, color) {
482
+ if (this._cachedStats[imageId] && this._isSameColor(this._cachedStats[imageId].color, color)) {
483
+ return this._cachedStats[imageId];
484
+ }
485
+ const overlays = await Promise.all(overlayMetadata.filter(overlay => overlay.pixelData).map(async (overlay, idx) => {
486
+ let pixelData = null;
487
+ if (overlay.pixelData.Value) {
488
+ pixelData = overlay.pixelData.Value;
489
+ } else if (overlay.pixelData instanceof Array) {
490
+ pixelData = overlay.pixelData[0];
491
+ } else if (overlay.pixelData.retrieveBulkData) {
492
+ pixelData = await overlay.pixelData.retrieveBulkData();
493
+ }
494
+ if (!pixelData) {
495
+ return;
496
+ }
497
+ const dataUrl = this._renderOverlayToDataUrl({
498
+ width: overlay.columns,
499
+ height: overlay.rows
500
+ }, color, pixelData);
501
+ return {
502
+ ...overlay,
503
+ _id: (0,utils/* guid */.M8)(),
504
+ dataUrl,
505
+ // this will be a data url expression of the rendered image
506
+ color
507
+ };
508
+ }));
509
+ this._cachedStats[imageId] = {
510
+ color: color,
511
+ overlays: overlays.filter(overlay => overlay)
512
+ };
513
+ return this._cachedStats[imageId];
514
+ }
515
+
516
+ /**
517
+ * compare two RGBA expression of colors.
518
+ *
519
+ * @param color1
520
+ * @param color2
521
+ * @returns
522
+ */
523
+ _isSameColor(color1, color2) {
524
+ return color1 && color2 && color1[0] === color2[0] && color1[1] === color2[1] && color1[2] === color2[2] && color1[3] === color2[3];
525
+ }
526
+
527
+ /**
528
+ * pixelData of overlayPlane module is an array of bits corresponding
529
+ * to each of the underlying pixels of the image.
530
+ * Let's create pixel data from bit array of overlay data
531
+ *
532
+ * @param pixelDataRaw
533
+ * @param color
534
+ * @returns
535
+ */
536
+ _renderOverlayToDataUrl(_ref, color, pixelDataRaw) {
537
+ let {
538
+ width,
539
+ height
540
+ } = _ref;
541
+ const pixelDataView = new DataView(pixelDataRaw);
542
+ const totalBits = width * height;
543
+ const canvas = document.createElement('canvas');
544
+ canvas.width = width;
545
+ canvas.height = height;
546
+ const ctx = canvas.getContext('2d');
547
+ ctx.clearRect(0, 0, width, height); // make it transparent
548
+ ctx.globalCompositeOperation = 'copy';
549
+ const imageData = ctx.getImageData(0, 0, width, height);
550
+ const data = imageData.data;
551
+ for (let i = 0, bitIdx = 0, byteIdx = 0; i < totalBits; i++) {
552
+ if (pixelDataView.getUint8(byteIdx) & 1 << bitIdx) {
553
+ data[i * 4] = color[0];
554
+ data[i * 4 + 1] = color[1];
555
+ data[i * 4 + 2] = color[2];
556
+ data[i * 4 + 3] = color[3];
557
+ }
558
+
559
+ // next bit, byte
560
+ if (bitIdx >= 7) {
561
+ bitIdx = 0;
562
+ byteIdx++;
563
+ } else {
564
+ bitIdx++;
565
+ }
566
+ }
567
+ ctx.putImageData(imageData, 0, 0);
568
+ return canvas.toDataURL();
569
+ }
570
+ }
571
+ ImageOverlayViewerTool.toolName = 'ImageOverlayViewer';
572
+ /* harmony default export */ const tools_ImageOverlayViewerTool = (ImageOverlayViewerTool);
361
573
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/initCornerstoneTools.js
362
574
 
363
575
 
576
+
364
577
  function initCornerstoneTools() {
365
578
  let configuration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
366
579
  dist_esm.CrosshairsTool.isAnnotation = false;
@@ -390,6 +603,10 @@ function initCornerstoneTools() {
390
603
  (0,dist_esm.addTool)(dist_esm.ReferenceLinesTool);
391
604
  (0,dist_esm.addTool)(tools_CalibrationLineTool);
392
605
  (0,dist_esm.addTool)(dist_esm.TrackballRotateTool);
606
+ (0,dist_esm.addTool)(dist_esm.CircleScissorsTool);
607
+ (0,dist_esm.addTool)(dist_esm.RectangleScissorsTool);
608
+ (0,dist_esm.addTool)(dist_esm.SphereScissorsTool);
609
+ (0,dist_esm.addTool)(tools_ImageOverlayViewerTool);
393
610
 
394
611
  // Modify annotation tools to use dashed lines on SR
395
612
  const annotationStyle = {
@@ -428,13 +645,17 @@ const toolNames = {
428
645
  SegmentationDisplay: dist_esm.SegmentationDisplayTool.toolName,
429
646
  ReferenceLines: dist_esm.ReferenceLinesTool.toolName,
430
647
  CalibrationLine: tools_CalibrationLineTool.toolName,
431
- TrackballRotateTool: dist_esm.TrackballRotateTool.toolName
648
+ TrackballRotateTool: dist_esm.TrackballRotateTool.toolName,
649
+ CircleScissors: dist_esm.CircleScissorsTool.toolName,
650
+ RectangleScissors: dist_esm.RectangleScissorsTool.toolName,
651
+ SphereScissors: dist_esm.SphereScissorsTool.toolName,
652
+ ImageOverlayViewer: tools_ImageOverlayViewerTool.toolName
432
653
  };
433
654
 
434
655
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js
435
656
  /* harmony default export */ const supportedTools = (['Length', 'EllipticalROI', 'CircleROI', 'Bidirectional', 'ArrowAnnotate', 'Angle', 'CobbAngle', 'Probe', 'RectangleROI', 'PlanarFreehandROI']);
436
657
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js
437
- var getSOPInstanceAttributes = __webpack_require__(63130);
658
+ var getSOPInstanceAttributes = __webpack_require__(87172);
438
659
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts
439
660
 
440
661
 
@@ -537,9 +758,9 @@ function getMappedAnnotations(annotation, displaySetService) {
537
758
  SeriesNumber
538
759
  } = displaySet;
539
760
  const {
540
- length
761
+ length,
762
+ unit = 'mm'
541
763
  } = targetStats;
542
- const unit = 'mm';
543
764
  annotations.push({
544
765
  SeriesInstanceUID,
545
766
  SOPInstanceUID,
@@ -566,10 +787,13 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
566
787
  values.push('Cornerstone:Length');
567
788
  mappedAnnotations.forEach(annotation => {
568
789
  const {
569
- length
790
+ length,
791
+ unit
570
792
  } = annotation;
571
- columns.push(`Length (mm)`);
793
+ columns.push(`Length`);
572
794
  values.push(length);
795
+ columns.push('Unit');
796
+ values.push(unit);
573
797
  });
574
798
  if (FrameOfReferenceUID) {
575
799
  columns.push('FrameOfReferenceUID');
@@ -598,7 +822,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
598
822
  length,
599
823
  SeriesNumber,
600
824
  SOPInstanceUID,
601
- frameNumber
825
+ frameNumber,
826
+ unit
602
827
  } = mappedAnnotations[0];
603
828
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
604
829
  let InstanceNumber;
@@ -607,16 +832,77 @@ function getDisplayText(mappedAnnotations, displaySet) {
607
832
  }
608
833
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
609
834
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
610
- if (length === null || length === undefined) return displayText;
835
+ if (length === null || length === undefined) {
836
+ return displayText;
837
+ }
611
838
  const roundedLength = src.utils.roundNumber(length, 2);
612
- displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
839
+ displayText.push(`${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
613
840
  return displayText;
614
841
  }
615
842
  /* harmony default export */ const measurementServiceMappings_Length = (Length);
843
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js
844
+ function getHandlesFromPoints(points) {
845
+ if (points.longAxis && points.shortAxis) {
846
+ const handles = {};
847
+ handles.start = points.longAxis[0];
848
+ handles.end = points.longAxis[1];
849
+ handles.perpendicularStart = points.longAxis[0];
850
+ handles.perpendicularEnd = points.longAxis[1];
851
+ return handles;
852
+ }
853
+ return points.map((p, i) => i % 10 === 0 ? {
854
+ start: p
855
+ } : {
856
+ end: p
857
+ }).reduce((obj, item) => Object.assign(obj, {
858
+ ...item
859
+ }), {});
860
+ }
861
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/selection.ts
862
+
863
+
864
+ /**
865
+ * Check whether an annotation from imaging library is selected or not.
866
+ * @param {string} annotationUID uid of imaging library annotation
867
+ * @returns boolean
868
+ */
869
+ function isAnnotationSelected(annotationUID) {
870
+ return dist_esm.annotation.selection.isAnnotationSelected(annotationUID);
871
+ }
872
+
873
+ /**
874
+ * Change an annotation from imaging library's selected property.
875
+ * @param annotationUID - uid of imaging library annotation
876
+ * @param selected - new value for selected
877
+ */
878
+ function setAnnotationSelected(annotationUID, selected) {
879
+ const isCurrentSelected = isAnnotationSelected(annotationUID);
880
+ // branch cut, avoid invoking imaging library unnecessarily.
881
+ if (isCurrentSelected !== selected) {
882
+ dist_esm.annotation.selection.setAnnotationSelected(annotationUID, selected);
883
+ }
884
+ }
885
+ function getFirstAnnotationSelected(element) {
886
+ const [selectedAnnotationUID] = dist_esm.annotation.selection.getAnnotationsSelected() || [];
887
+ if (selectedAnnotationUID) {
888
+ return dist_esm.annotation.state.getAnnotation(selectedAnnotationUID);
889
+ }
890
+ }
891
+
892
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getDisplayUnit.js
893
+ const getDisplayUnit = unit => unit == null ? '' : unit;
894
+ /* harmony default export */ const utils_getDisplayUnit = (getDisplayUnit);
895
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/index.ts
896
+
897
+
898
+
899
+
900
+
616
901
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.ts
617
902
 
618
903
 
619
904
 
905
+
620
906
  const Bidirectional = {
621
907
  toAnnotation: measurement => {},
622
908
  toMeasurement: (csToolsEventDetail, displaySetService, cornerstoneViewportService, getValueTypeFromToolType) => {
@@ -711,9 +997,9 @@ function Bidirectional_getMappedAnnotations(annotation, displaySetService) {
711
997
  } = displaySet;
712
998
  const {
713
999
  length,
714
- width
1000
+ width,
1001
+ unit
715
1002
  } = targetStats;
716
- const unit = 'mm';
717
1003
  annotations.push({
718
1004
  SeriesInstanceUID,
719
1005
  SOPInstanceUID,
@@ -742,10 +1028,11 @@ function Bidirectional_getReport(mappedAnnotations, points, FrameOfReferenceUID)
742
1028
  mappedAnnotations.forEach(annotation => {
743
1029
  const {
744
1030
  length,
745
- width
1031
+ width,
1032
+ unit
746
1033
  } = annotation;
747
- columns.push(`Length (mm)`, `Width (mm)`);
748
- values.push(length, width);
1034
+ columns.push(`Length`, `Width`, 'Unit');
1035
+ values.push(length, width, unit);
749
1036
  });
750
1037
  if (FrameOfReferenceUID) {
751
1038
  columns.push('FrameOfReferenceUID');
@@ -773,6 +1060,7 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
773
1060
  const {
774
1061
  length,
775
1062
  width,
1063
+ unit,
776
1064
  SeriesNumber,
777
1065
  SOPInstanceUID,
778
1066
  frameNumber
@@ -786,22 +1074,11 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
786
1074
  }
787
1075
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
788
1076
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
789
- displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
790
- displayText.push(`W: ${roundedWidth} mm`);
1077
+ displayText.push(`L: ${roundedLength} ${utils_getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`);
1078
+ displayText.push(`W: ${roundedWidth} ${utils_getDisplayUnit(unit)}`);
791
1079
  return displayText;
792
1080
  }
793
1081
  /* harmony default export */ const measurementServiceMappings_Bidirectional = (Bidirectional);
794
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getModalityUnit.js
795
- function getModalityUnit(modality) {
796
- if (modality === 'CT') {
797
- return 'HU';
798
- } else if (modality === 'PT') {
799
- return 'SUV';
800
- } else {
801
- return '';
802
- }
803
- }
804
- /* harmony default export */ const utils_getModalityUnit = (getModalityUnit);
805
1082
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts
806
1083
 
807
1084
 
@@ -904,16 +1181,18 @@ function EllipticalROI_getMappedAnnotations(annotation, displaySetService) {
904
1181
  stdDev,
905
1182
  max,
906
1183
  area,
907
- Modality
1184
+ Modality,
1185
+ areaUnit,
1186
+ modalityUnit
908
1187
  } = targetStats;
909
- const unit = utils_getModalityUnit(Modality);
910
1188
  annotations.push({
911
1189
  SeriesInstanceUID,
912
1190
  SOPInstanceUID,
913
1191
  SeriesNumber,
914
1192
  frameNumber,
915
1193
  Modality,
916
- unit,
1194
+ unit: modalityUnit,
1195
+ areaUnit,
917
1196
  mean,
918
1197
  stdDev,
919
1198
  max,
@@ -941,13 +1220,14 @@ function EllipticalROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
941
1220
  stdDev,
942
1221
  max,
943
1222
  area,
944
- unit
1223
+ unit,
1224
+ areaUnit
945
1225
  } = annotation;
946
1226
  if (!mean || !unit || !max || !area) {
947
1227
  return;
948
1228
  }
949
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
950
- values.push(max, mean, stdDev, area);
1229
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1230
+ values.push(max, mean, stdDev, area, areaUnit);
951
1231
  });
952
1232
  if (FrameOfReferenceUID) {
953
1233
  columns.push('FrameOfReferenceUID');
@@ -975,7 +1255,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
975
1255
  const {
976
1256
  area,
977
1257
  SOPInstanceUID,
978
- frameNumber
1258
+ frameNumber,
1259
+ areaUnit
979
1260
  } = mappedAnnotations[0];
980
1261
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
981
1262
  let InstanceNumber;
@@ -984,10 +1265,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
984
1265
  }
985
1266
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
986
1267
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
987
-
988
- // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
989
- const roundedArea = src.utils.roundNumber(area || 0, 2);
990
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
1268
+ const roundedArea = src.utils.roundNumber(area, 2);
1269
+ displayText.push(`${roundedArea} ${utils_getDisplayUnit(areaUnit)}`);
991
1270
 
992
1271
  // Todo: we need a better UI for displaying all these information
993
1272
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -999,7 +1278,7 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
999
1278
  let maxStr = '';
1000
1279
  if (max) {
1001
1280
  const roundedMax = src.utils.roundNumber(max, 2);
1002
- maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
1281
+ maxStr = `Max: ${roundedMax} <small>${utils_getDisplayUnit(unit)}</small> `;
1003
1282
  }
1004
1283
  const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
1005
1284
  if (!displayText.includes(str)) {
@@ -1111,20 +1390,22 @@ function CircleROI_getMappedAnnotations(annotation, DisplaySetService) {
1111
1390
  stdDev,
1112
1391
  max,
1113
1392
  area,
1114
- Modality
1393
+ Modality,
1394
+ areaUnit,
1395
+ modalityUnit
1115
1396
  } = targetStats;
1116
- const unit = utils_getModalityUnit(Modality);
1117
1397
  annotations.push({
1118
1398
  SeriesInstanceUID,
1119
1399
  SOPInstanceUID,
1120
1400
  SeriesNumber,
1121
1401
  frameNumber,
1122
1402
  Modality,
1123
- unit,
1403
+ unit: modalityUnit,
1124
1404
  mean,
1125
1405
  stdDev,
1126
1406
  max,
1127
- area
1407
+ area,
1408
+ areaUnit
1128
1409
  });
1129
1410
  });
1130
1411
  return annotations;
@@ -1148,13 +1429,14 @@ function CircleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID) {
1148
1429
  stdDev,
1149
1430
  max,
1150
1431
  area,
1151
- unit
1432
+ unit,
1433
+ areaUnit
1152
1434
  } = annotation;
1153
1435
  if (!mean || !unit || !max || !area) {
1154
1436
  return;
1155
1437
  }
1156
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1157
- values.push(max, mean, stdDev, area);
1438
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1439
+ values.push(max, mean, stdDev, area, areaUnit);
1158
1440
  });
1159
1441
  if (FrameOfReferenceUID) {
1160
1442
  columns.push('FrameOfReferenceUID');
@@ -1182,7 +1464,8 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1182
1464
  const {
1183
1465
  area,
1184
1466
  SOPInstanceUID,
1185
- frameNumber
1467
+ frameNumber,
1468
+ areaUnit
1186
1469
  } = mappedAnnotations[0];
1187
1470
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
1188
1471
  let InstanceNumber;
@@ -1194,7 +1477,7 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1194
1477
 
1195
1478
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
1196
1479
  const roundedArea = src.utils.roundNumber(area || 0, 2);
1197
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
1480
+ displayText.push(`${roundedArea} ${utils_getDisplayUnit(areaUnit)}`);
1198
1481
 
1199
1482
  // Todo: we need a better UI for displaying all these information
1200
1483
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -1206,7 +1489,7 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1206
1489
  let maxStr = '';
1207
1490
  if (max) {
1208
1491
  const roundedMax = src.utils.roundNumber(max, 2);
1209
- maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
1492
+ maxStr = `Max: ${roundedMax} <small>${utils_getDisplayUnit(unit)}</small> `;
1210
1493
  }
1211
1494
  const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
1212
1495
  if (!displayText.includes(str)) {
@@ -1345,6 +1628,7 @@ function ArrowAnnotate_getDisplayText(mappedAnnotations, displaySet) {
1345
1628
 
1346
1629
 
1347
1630
 
1631
+
1348
1632
  const CobbAngle = {
1349
1633
  toAnnotation: measurement => {},
1350
1634
  /**
@@ -1515,9 +1799,11 @@ function CobbAngle_getDisplayText(mappedAnnotations, displaySet) {
1515
1799
  }
1516
1800
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1517
1801
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1518
- if (angle === undefined) return displayText;
1802
+ if (angle === undefined) {
1803
+ return displayText;
1804
+ }
1519
1805
  const roundedAngle = src.utils.roundNumber(angle, 2);
1520
- displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1806
+ displayText.push(`${roundedAngle} ${utils_getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`);
1521
1807
  return displayText;
1522
1808
  }
1523
1809
  /* harmony default export */ const measurementServiceMappings_CobbAngle = (CobbAngle);
@@ -1525,6 +1811,7 @@ function CobbAngle_getDisplayText(mappedAnnotations, displaySet) {
1525
1811
 
1526
1812
 
1527
1813
 
1814
+
1528
1815
  const Angle = {
1529
1816
  toAnnotation: measurement => {},
1530
1817
  /**
@@ -1695,9 +1982,11 @@ function Angle_getDisplayText(mappedAnnotations, displaySet) {
1695
1982
  }
1696
1983
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1697
1984
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1698
- if (angle === undefined) return displayText;
1985
+ if (angle === undefined) {
1986
+ return displayText;
1987
+ }
1699
1988
  const roundedAngle = src.utils.roundNumber(angle, 2);
1700
- displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1989
+ displayText.push(`${roundedAngle} ${utils_getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`);
1701
1990
  return displayText;
1702
1991
  }
1703
1992
  /* harmony default export */ const measurementServiceMappings_Angle = (Angle);
@@ -1937,20 +2226,22 @@ function RectangleROI_getMappedAnnotations(annotation, DisplaySetService) {
1937
2226
  stdDev,
1938
2227
  max,
1939
2228
  area,
1940
- Modality
2229
+ Modality,
2230
+ modalityUnit,
2231
+ areaUnit
1941
2232
  } = targetStats;
1942
- const unit = utils_getModalityUnit(Modality);
1943
2233
  annotations.push({
1944
2234
  SeriesInstanceUID,
1945
2235
  SOPInstanceUID,
1946
2236
  SeriesNumber,
1947
2237
  frameNumber,
1948
2238
  Modality,
1949
- unit,
2239
+ unit: modalityUnit,
1950
2240
  mean,
1951
2241
  stdDev,
1952
2242
  max,
1953
- area
2243
+ area,
2244
+ areaUnit
1954
2245
  });
1955
2246
  });
1956
2247
  return annotations;
@@ -1974,13 +2265,14 @@ function RectangleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
1974
2265
  stdDev,
1975
2266
  max,
1976
2267
  area,
1977
- unit
2268
+ unit,
2269
+ areaUnit
1978
2270
  } = annotation;
1979
2271
  if (!mean || !unit || !max || !area) {
1980
2272
  return;
1981
2273
  }
1982
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1983
- values.push(max, mean, stdDev, area);
2274
+ columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit');
2275
+ values.push(max, mean, stdDev, unit, area, areaUnit);
1984
2276
  });
1985
2277
  if (FrameOfReferenceUID) {
1986
2278
  columns.push('FrameOfReferenceUID');
@@ -2008,7 +2300,8 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2008
2300
  const {
2009
2301
  area,
2010
2302
  SOPInstanceUID,
2011
- frameNumber
2303
+ frameNumber,
2304
+ areaUnit
2012
2305
  } = mappedAnnotations[0];
2013
2306
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
2014
2307
  let InstanceNumber;
@@ -2020,7 +2313,7 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2020
2313
 
2021
2314
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
2022
2315
  const roundedArea = src.utils.roundNumber(area || 0, 2);
2023
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
2316
+ displayText.push(`${roundedArea} ${utils_getDisplayUnit(areaUnit)}`);
2024
2317
 
2025
2318
  // Todo: we need a better UI for displaying all these information
2026
2319
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -2032,7 +2325,7 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2032
2325
  let maxStr = '';
2033
2326
  if (max) {
2034
2327
  const roundedMax = src.utils.roundNumber(max, 2);
2035
- maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
2328
+ maxStr = `Max: ${roundedMax} <small>${utils_getDisplayUnit(unit)}</small> `;
2036
2329
  }
2037
2330
  const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
2038
2331
  if (!displayText.includes(str)) {
@@ -2515,7 +2808,7 @@ function getInterleavedFrames(imageIds) {
2515
2808
  }
2516
2809
  while (!prefetchQueuedFilled.currentPositionDownToMinimum || !prefetchQueuedFilled.currentPositionUpToMaximum) {
2517
2810
  if (!prefetchQueuedFilled.currentPositionDownToMinimum) {
2518
- // Add imageId bellow
2811
+ // Add imageId below
2519
2812
  lowerImageIdIndex--;
2520
2813
  imageIdsToPrefetch.push({
2521
2814
  imageId: imageIds[lowerImageIdIndex],
@@ -2540,7 +2833,7 @@ function getInterleavedFrames(imageIds) {
2540
2833
  return imageIdsToPrefetch;
2541
2834
  }
2542
2835
  // EXTERNAL MODULE: ../../../node_modules/lodash/lodash.js
2543
- var lodash = __webpack_require__(92891);
2836
+ var lodash = __webpack_require__(44379);
2544
2837
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/interleaveCenterLoader.ts
2545
2838
 
2546
2839
 
@@ -2730,8 +3023,12 @@ function getNthFrames(imageIds) {
2730
3023
  * @returns [] reordered to be breadth first traversal of lists
2731
3024
  */
2732
3025
  function interleave(lists) {
2733
- if (!lists || !lists.length) return [];
2734
- if (lists.length === 1) return lists[0];
3026
+ if (!lists || !lists.length) {
3027
+ return [];
3028
+ }
3029
+ if (lists.length === 1) {
3030
+ return lists[0];
3031
+ }
2735
3032
  console.time('interleave');
2736
3033
  const useLists = [...lists];
2737
3034
  const ret = [];
@@ -3004,6 +3301,7 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3004
3301
  commands: [{
3005
3302
  commandName: 'showCornerstoneContextMenu',
3006
3303
  commandOptions: {
3304
+ requireNearbyToolData: true,
3007
3305
  menuId: 'measurementsContextMenu'
3008
3306
  }
3009
3307
  }]
@@ -3020,9 +3318,15 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3020
3318
  function getEventName(evt) {
3021
3319
  const button = evt.detail.event.which;
3022
3320
  const nameArr = [];
3023
- if (evt.detail.event.altKey) nameArr.push('alt');
3024
- if (evt.detail.event.ctrlKey) nameArr.push('ctrl');
3025
- if (evt.detail.event.shiftKey) nameArr.push('shift');
3321
+ if (evt.detail.event.altKey) {
3322
+ nameArr.push('alt');
3323
+ }
3324
+ if (evt.detail.event.ctrlKey) {
3325
+ nameArr.push('ctrl');
3326
+ }
3327
+ if (evt.detail.event.shiftKey) {
3328
+ nameArr.push('shift');
3329
+ }
3026
3330
  nameArr.push('button');
3027
3331
  nameArr.push(button);
3028
3332
  return nameArr.join('');
@@ -3040,9 +3344,18 @@ function initContextMenu(_ref) {
3040
3344
  const cornerstoneViewportHandleEvent = (name, evt) => {
3041
3345
  const customizations = customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
3042
3346
  const toRun = customizations[name];
3043
- console.log('initContextMenu::cornerstoneViewportHandleEvent', name, toRun);
3347
+ if (!toRun) {
3348
+ return;
3349
+ }
3350
+
3351
+ // only find nearbyToolData if required, for the click (which closes the context menu
3352
+ // we don't need to find nearbyToolData)
3353
+ let nearbyToolData = null;
3354
+ if (toRun.commands.some(command => command.commandOptions?.requireNearbyToolData)) {
3355
+ nearbyToolData = findNearbyToolData(commandsManager, evt);
3356
+ }
3044
3357
  const options = {
3045
- nearbyToolData: findNearbyToolData(commandsManager, evt),
3358
+ nearbyToolData,
3046
3359
  event: evt
3047
3360
  };
3048
3361
  commandsManager.run(toRun, options);
@@ -3057,10 +3370,11 @@ function initContextMenu(_ref) {
3057
3370
  element
3058
3371
  } = evt.detail;
3059
3372
  const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
3060
- if (!viewportInfo) return;
3061
- const viewportIndex = viewportInfo.getViewportIndex();
3373
+ if (!viewportInfo) {
3374
+ return;
3375
+ }
3062
3376
  // TODO check update upstream
3063
- (0,state/* setEnabledElement */.Yc)(viewportIndex, element);
3377
+ (0,state/* setEnabledElement */.Yc)(viewportId, element);
3064
3378
  element.addEventListener(cs3DToolsEvents.MOUSE_CLICK, cornerstoneViewportHandleClick);
3065
3379
  }
3066
3380
  function elementDisabledHandler(evt) {
@@ -3094,9 +3408,15 @@ const DEFAULT_DOUBLE_CLICK = {
3094
3408
  */
3095
3409
  function getDoubleClickEventName(evt) {
3096
3410
  const nameArr = [];
3097
- if (evt.detail.event.altKey) nameArr.push('alt');
3098
- if (evt.detail.event.ctrlKey) nameArr.push('ctrl');
3099
- if (evt.detail.event.shiftKey) nameArr.push('shift');
3411
+ if (evt.detail.event.altKey) {
3412
+ nameArr.push('alt');
3413
+ }
3414
+ if (evt.detail.event.ctrlKey) {
3415
+ nameArr.push('ctrl');
3416
+ }
3417
+ if (evt.detail.event.shiftKey) {
3418
+ nameArr.push('shift');
3419
+ }
3100
3420
  nameArr.push('doubleClick');
3101
3421
  return nameArr.join('');
3102
3422
  }
@@ -3137,6 +3457,49 @@ function initDoubleClick(_ref) {
3137
3457
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
3138
3458
  }
3139
3459
  /* harmony default export */ const src_initDoubleClick = (initDoubleClick);
3460
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/initViewTiming.ts
3461
+
3462
+
3463
+ const {
3464
+ TimingEnum
3465
+ } = src.Types;
3466
+ const IMAGE_TIMING_KEYS = [TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES, TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE, TimingEnum.STUDY_TO_FIRST_IMAGE];
3467
+ const imageTiming = {
3468
+ viewportsWaiting: 0
3469
+ };
3470
+
3471
+ /**
3472
+ * Defines the initial view timing reporting.
3473
+ * This allows knowing how many viewports are waiting for initial views and
3474
+ * when the IMAGE_RENDERED gets sent out.
3475
+ * The first image rendered will fire the FIRST_IMAGE timeEnd logs, while
3476
+ * the last of the enabled viewport will fire the ALL_IMAGES timeEnd logs.
3477
+ *
3478
+ */
3479
+
3480
+ function initViewTiming(_ref) {
3481
+ let {
3482
+ element
3483
+ } = _ref;
3484
+ if (!IMAGE_TIMING_KEYS.find(key => src/* log */.cM.timingKeys[key])) {
3485
+ return;
3486
+ }
3487
+ imageTiming.viewportsWaiting += 1;
3488
+ element.addEventListener(esm.EVENTS.IMAGE_RENDERED, imageRenderedListener);
3489
+ }
3490
+ function imageRenderedListener(evt) {
3491
+ if (evt.detail.viewportStatus === 'preRender') {
3492
+ return;
3493
+ }
3494
+ src/* log */.cM.timeEnd(TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
3495
+ src/* log */.cM.timeEnd(TimingEnum.STUDY_TO_FIRST_IMAGE);
3496
+ src/* log */.cM.timeEnd(TimingEnum.SCRIPT_TO_VIEW);
3497
+ imageTiming.viewportsWaiting -= 1;
3498
+ evt.detail.element.removeEventListener(esm.EVENTS.IMAGE_RENDERED, imageRenderedListener);
3499
+ if (!imageTiming.viewportsWaiting) {
3500
+ src/* log */.cM.timeEnd(TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
3501
+ }
3502
+ }
3140
3503
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/init.tsx
3141
3504
 
3142
3505
 
@@ -3155,6 +3518,7 @@ function initDoubleClick(_ref) {
3155
3518
 
3156
3519
 
3157
3520
 
3521
+
3158
3522
  // TODO: Cypress tests are currently grabbing this from the window?
3159
3523
  window.cornerstone = esm;
3160
3524
  window.cornerstoneTools = dist_esm;
@@ -3169,7 +3533,24 @@ async function init(_ref) {
3169
3533
  configuration,
3170
3534
  appConfig
3171
3535
  } = _ref;
3172
- await (0,esm.init)();
3536
+ // Note: this should run first before initializing the cornerstone
3537
+ // DO NOT CHANGE THE ORDER
3538
+ const value = appConfig.useSharedArrayBuffer;
3539
+ let sharedArrayBufferDisabled = false;
3540
+ if (value === 'AUTO') {
3541
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.AUTO);
3542
+ } else if (value === 'FALSE' || value === false) {
3543
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.FALSE);
3544
+ sharedArrayBufferDisabled = true;
3545
+ } else {
3546
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.TRUE);
3547
+ }
3548
+ await (0,esm.init)({
3549
+ rendering: {
3550
+ preferSizeOverAccuracy: Boolean(appConfig.use16BitDataType),
3551
+ useNorm16Texture: Boolean(appConfig.use16BitDataType)
3552
+ }
3553
+ });
3173
3554
 
3174
3555
  // For debugging e2e tests that are failing on CI
3175
3556
  esm.setUseCPURendering(Boolean(appConfig.useCPURendering));
@@ -3181,34 +3562,35 @@ async function init(_ref) {
3181
3562
  }
3182
3563
  });
3183
3564
 
3184
- // For debugging large datasets
3185
- const MAX_CACHE_SIZE_1GB = 1073741824;
3186
- const maxCacheSize = appConfig.maxCacheSize;
3187
- esm.cache.setMaxCacheSize(maxCacheSize ? maxCacheSize : MAX_CACHE_SIZE_1GB);
3565
+ // For debugging large datasets, otherwise prefer the defaults
3566
+ const {
3567
+ maxCacheSize
3568
+ } = appConfig;
3569
+ if (maxCacheSize) {
3570
+ esm.cache.setMaxCacheSize(maxCacheSize);
3571
+ }
3188
3572
  initCornerstoneTools();
3189
3573
  esm.Settings.getRuntimeSettings().set('useCursors', Boolean(appConfig.useCursors));
3190
3574
  const {
3191
3575
  userAuthenticationService,
3192
- measurementService,
3193
3576
  customizationService,
3194
- displaySetService,
3195
- uiDialogService,
3196
3577
  uiModalService,
3197
3578
  uiNotificationService,
3198
3579
  cineService,
3199
3580
  cornerstoneViewportService,
3200
3581
  hangingProtocolService,
3201
3582
  toolGroupService,
3583
+ toolbarService,
3202
3584
  viewportGridService,
3203
3585
  stateSyncService
3204
3586
  } = servicesManager.services;
3205
3587
  window.services = servicesManager.services;
3206
3588
  window.extensionManager = extensionManager;
3207
3589
  window.commandsManager = commandsManager;
3208
- if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated) {
3590
+ if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated && !sharedArrayBufferDisabled) {
3209
3591
  uiNotificationService.show({
3210
3592
  title: 'Cross Origin Isolation',
3211
- message: 'Cross Origin Isolation is not enabled, volume rendering will not work (e.g., MPR)',
3593
+ message: 'Cross Origin Isolation is not enabled, read more about it here: https://docs.ohif.org/faq/',
3212
3594
  type: 'warning'
3213
3595
  });
3214
3596
  }
@@ -3254,7 +3636,7 @@ async function init(_ref) {
3254
3636
  thumbnail: appConfig?.maxNumRequests?.thumbnail || 75,
3255
3637
  prefetch: appConfig?.maxNumRequests?.prefetch || 10
3256
3638
  };
3257
- initWADOImageLoader(userAuthenticationService, appConfig);
3639
+ initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
3258
3640
 
3259
3641
  /* Measurement Service */
3260
3642
  this.measurementServiceSource = connectToolsToMeasurementService(servicesManager);
@@ -3289,11 +3671,51 @@ async function init(_ref) {
3289
3671
  customizationService,
3290
3672
  commandsManager
3291
3673
  });
3292
- const newStackCallback = evt => {
3674
+
3675
+ /**
3676
+ * When a viewport gets a new display set, this call will go through all the
3677
+ * active tools in the toolbar, and call any commands registered in the
3678
+ * toolbar service with a callback to re-enable on displaying the viewport.
3679
+ */
3680
+ const toolbarEventListener = evt => {
3293
3681
  const {
3294
3682
  element
3295
3683
  } = evt.detail;
3296
- dist_esm.utilities.stackPrefetch.enable(element);
3684
+ const activeTools = toolbarService.getActiveTools();
3685
+ activeTools.forEach(tool => {
3686
+ const toolData = toolbarService.getNestedButton(tool);
3687
+ const commands = toolData?.listeners?.[evt.type];
3688
+ commandsManager.run(commands, {
3689
+ element,
3690
+ evt
3691
+ });
3692
+ });
3693
+ };
3694
+
3695
+ /** Listens for active viewport events and fires the toolbar listeners */
3696
+ const activeViewportEventListener = evt => {
3697
+ const {
3698
+ viewportId
3699
+ } = evt;
3700
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
3701
+ const activeTools = toolbarService.getActiveTools();
3702
+ activeTools.forEach(tool => {
3703
+ if (!toolGroup?._toolInstances?.[tool]) {
3704
+ return;
3705
+ }
3706
+
3707
+ // check if tool is active on the new viewport
3708
+ const toolEnabled = toolGroup._toolInstances[tool].mode === dist_esm.Enums.ToolModes.Enabled;
3709
+ if (!toolEnabled) {
3710
+ return;
3711
+ }
3712
+ const button = toolbarService.getNestedButton(tool);
3713
+ const commands = button?.listeners?.[evt.type];
3714
+ commandsManager.run(commands, {
3715
+ viewportId,
3716
+ evt
3717
+ });
3718
+ });
3297
3719
  };
3298
3720
  const resetCrosshairs = evt => {
3299
3721
  const {
@@ -3316,12 +3738,22 @@ async function init(_ref) {
3316
3738
  toolGroup.setToolEnabled('Crosshairs');
3317
3739
  }
3318
3740
  };
3741
+ esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, evt => {
3742
+ const {
3743
+ element
3744
+ } = evt.detail;
3745
+ dist_esm.utilities.stackContextPrefetch.enable(element);
3746
+ });
3319
3747
  function elementEnabledHandler(evt) {
3320
3748
  const {
3321
3749
  element
3322
3750
  } = evt.detail;
3323
3751
  element.addEventListener(esm.EVENTS.CAMERA_RESET, resetCrosshairs);
3324
- esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, newStackCallback);
3752
+ esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, toolbarEventListener);
3753
+ initViewTiming({
3754
+ element,
3755
+ eventTarget: esm.eventTarget
3756
+ });
3325
3757
  }
3326
3758
  function elementDisabledHandler(evt) {
3327
3759
  const {
@@ -3338,31 +3770,7 @@ async function init(_ref) {
3338
3770
 
3339
3771
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
3340
3772
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
3341
- viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_INDEX_CHANGED, _ref2 => {
3342
- let {
3343
- viewportIndex,
3344
- viewportId
3345
- } = _ref2;
3346
- viewportId = viewportId || `viewport-${viewportIndex}`;
3347
- const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
3348
- if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
3349
- return;
3350
- }
3351
-
3352
- // check if reference lines are active
3353
- const referenceLinesEnabled = toolGroup._toolInstances['ReferenceLines'].mode === dist_esm.Enums.ToolModes.Enabled;
3354
- if (!referenceLinesEnabled) {
3355
- return;
3356
- }
3357
- toolGroup.setToolConfiguration(dist_esm.ReferenceLinesTool.toolName, {
3358
- sourceViewportId: viewportId
3359
- }, true // overwrite
3360
- );
3361
-
3362
- // make sure to set it to enabled again since we want to recalculate
3363
- // the source-target lines
3364
- toolGroup.setToolEnabled(dist_esm.ReferenceLinesTool.toolName);
3365
- });
3773
+ viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, activeViewportEventListener);
3366
3774
  }
3367
3775
  function CPUModal() {
3368
3776
  return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement("p", null, "Your computer does not have enough GPU power to support the default GPU rendering mode. OHIF has switched to CPU rendering mode. Please note that CPU rendering does not support all features such as Volume Rendering, Multiplanar Reconstruction, and Segmentation Overlays."));
@@ -3387,12 +3795,12 @@ function _showCPURenderingModal(uiModalService, hangingProtocolService) {
3387
3795
  });
3388
3796
  }
3389
3797
  // EXTERNAL MODULE: ../../../node_modules/react-dropzone/dist/es/index.js + 5 modules
3390
- var es = __webpack_require__(58591);
3798
+ var es = __webpack_require__(74834);
3391
3799
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
3392
- var prop_types = __webpack_require__(60216);
3800
+ var prop_types = __webpack_require__(3827);
3393
3801
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
3394
3802
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
3395
- var classnames = __webpack_require__(40841);
3803
+ var classnames = __webpack_require__(44921);
3396
3804
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
3397
3805
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/DicomFileUploader.ts
3398
3806
 
@@ -3541,7 +3949,9 @@ class DicomFileUploader extends src/* PubSubService */.hC {
3541
3949
  request.addEventListener('loadend', cleanUpCallback);
3542
3950
  }
3543
3951
  _checkDicomFile(arrayBuffer) {
3544
- if (arrayBuffer.length <= 132) return false;
3952
+ if (arrayBuffer.length <= 132) {
3953
+ return false;
3954
+ }
3545
3955
  const arr = new Uint8Array(arrayBuffer.slice(128, 132));
3546
3956
  // bytes from 128 to 132 must be "DICM"
3547
3957
  return Array.from('DICM').every((char, i) => char.charCodeAt(0) === arr[i]);
@@ -3560,7 +3970,6 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3560
3970
  const [percentComplete, setPercentComplete] = (0,react.useState)(dicomFileUploader.getPercentComplete());
3561
3971
  const [failedReason, setFailedReason] = (0,react.useState)('');
3562
3972
  const [status, setStatus] = (0,react.useState)(dicomFileUploader.getStatus());
3563
- console.info(`${dicomFileUploader.getFileId()}`);
3564
3973
  const isComplete = (0,react.useCallback)(() => {
3565
3974
  return status === UploadStatus.Failed || status === UploadStatus.Cancelled || status === UploadStatus.Success;
3566
3975
  }, [status]);
@@ -3601,25 +4010,25 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3601
4010
  }
3602
4011
  };
3603
4012
  return /*#__PURE__*/react.createElement("div", {
3604
- className: "flex w-full p-2.5 text-lg min-h-14 items-center border-b border-secondary-light overflow-hidden"
4013
+ className: "min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg"
3605
4014
  }, /*#__PURE__*/react.createElement("div", {
3606
- className: "flex flex-col gap-1 self-top w-0 grow shrink"
4015
+ className: "self-top flex w-0 shrink grow flex-col gap-1"
3607
4016
  }, /*#__PURE__*/react.createElement("div", {
3608
4017
  className: "flex gap-4"
3609
4018
  }, /*#__PURE__*/react.createElement("div", {
3610
- className: "flex w-6 justify-center items-center shrink-0"
4019
+ className: "flex w-6 shrink-0 items-center justify-center"
3611
4020
  }, getStatusIcon()), /*#__PURE__*/react.createElement("div", {
3612
- className: "text-ellipsis whitespace-nowrap overflow-hidden"
4021
+ className: "overflow-hidden text-ellipsis whitespace-nowrap"
3613
4022
  }, dicomFileUploader.getFileName())), failedReason && /*#__PURE__*/react.createElement("div", {
3614
4023
  className: "pl-10"
3615
4024
  }, failedReason)), /*#__PURE__*/react.createElement("div", {
3616
- className: "w-24 flex items-center"
4025
+ className: "flex w-24 items-center"
3617
4026
  }, !isComplete() && /*#__PURE__*/react.createElement(react.Fragment, null, dicomFileUploader.getStatus() === UploadStatus.InProgress && /*#__PURE__*/react.createElement("div", {
3618
4027
  className: "w-10 text-right"
3619
4028
  }, percentComplete, "%"), /*#__PURE__*/react.createElement("div", {
3620
- className: "flex cursor-pointer ml-auto"
4029
+ className: "ml-auto flex cursor-pointer"
3621
4030
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
3622
- className: "self-center text-primary-active",
4031
+ className: "text-primary-active self-center",
3623
4032
  name: "close",
3624
4033
  onClick: cancelUpload
3625
4034
  })))));
@@ -3846,7 +4255,7 @@ function DicomUploadProgress(_ref) {
3846
4255
  }, []);
3847
4256
  const getNumCompletedAndTimeRemainingComponent = () => {
3848
4257
  return /*#__PURE__*/react.createElement("div", {
3849
- className: "text-lg px-1 pb-4 h-14 flex bg-primary-dark items-center"
4258
+ className: "bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg"
3850
4259
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("span", {
3851
4260
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3852
4261
  }, `${dicomFileUploaderArr.length} ${dicomFileUploaderArr.length > 1 ? 'files' : 'file'} completed.`), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
@@ -3861,13 +4270,13 @@ function DicomUploadProgress(_ref) {
3861
4270
  }, ' files completed.', "\xA0"), /*#__PURE__*/react.createElement("span", {
3862
4271
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3863
4272
  }, timeRemaining ? `Less than ${getFormattedTimeRemaining()} remaining. ` : ''), /*#__PURE__*/react.createElement("span", {
3864
- className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES, 'cursor-pointer text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto'),
4273
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES, 'text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'),
3865
4274
  onClick: cancelAllUploads
3866
4275
  }, "Cancel All Uploads")));
3867
4276
  };
3868
4277
  const getShowFailedOnlyIconComponent = () => {
3869
4278
  return /*#__PURE__*/react.createElement("div", {
3870
- className: "ml-auto flex justify-center w-6"
4279
+ className: "ml-auto flex w-6 justify-center"
3871
4280
  }, numFails > 0 && /*#__PURE__*/react.createElement("div", {
3872
4281
  onClick: () => setShowFailedOnly(currentShowFailedOnly => !currentShowFailedOnly)
3873
4282
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
@@ -3877,28 +4286,28 @@ function DicomUploadProgress(_ref) {
3877
4286
  };
3878
4287
  const getPercentCompleteComponent = () => {
3879
4288
  return /*#__PURE__*/react.createElement("div", {
3880
- className: "overflow-y-scroll ohif-scrollbar px-2 border-b border-secondary-light"
4289
+ className: "ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2"
3881
4290
  }, /*#__PURE__*/react.createElement("div", {
3882
- className: "flex w-full p-2.5 items-center min-h-14"
4291
+ className: "min-h-14 flex w-full items-center p-2.5"
3883
4292
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3884
- className: "text-xl text-primary-light"
4293
+ className: "text-primary-light text-xl"
3885
4294
  }, numFails > 0 ? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!` : 'Completed!'), getShowFailedOnlyIconComponent()) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3886
4295
  ref: progressBarContainerRef,
3887
4296
  className: "flex-grow"
3888
4297
  }, /*#__PURE__*/react.createElement(ui_src/* ProgressLoadingBar */.YE, {
3889
4298
  progress: showInfiniteProgressBar() ? undefined : Math.min(100, percentComplete)
3890
4299
  })), /*#__PURE__*/react.createElement("div", {
3891
- className: "w-24 ml-1 flex items-center"
4300
+ className: "ml-1 flex w-24 items-center"
3892
4301
  }, /*#__PURE__*/react.createElement("div", {
3893
4302
  className: "w-10 text-right"
3894
4303
  }, `${getPercentCompleteRounded()}%`), getShowFailedOnlyIconComponent()))));
3895
4304
  };
3896
4305
  return /*#__PURE__*/react.createElement("div", {
3897
- className: "flex flex-col grow"
4306
+ className: "flex grow flex-col"
3898
4307
  }, getNumCompletedAndTimeRemainingComponent(), /*#__PURE__*/react.createElement("div", {
3899
- className: "flex flex-col bg-black text-lg overflow-hidden grow"
4308
+ className: "flex grow flex-col overflow-hidden bg-black text-lg"
3900
4309
  }, getPercentCompleteComponent(), /*#__PURE__*/react.createElement("div", {
3901
- className: "overflow-y-scroll ohif-scrollbar px-2 grow h-1"
4310
+ className: "ohif-scrollbar h-1 grow overflow-y-scroll px-2"
3902
4311
  }, dicomFileUploaderArr.filter(dicomFileUploader => !showFailedOnly || dicomFileUploader.getStatus() === UploadStatus.Failed).map(dicomFileUploader => /*#__PURE__*/react.createElement(DicomUpload_DicomUploadProgressItem, {
3903
4312
  key: dicomFileUploader.getFileId(),
3904
4313
  dicomFileUploader: dicomFileUploader
@@ -3945,7 +4354,7 @@ function DicomUpload(_ref) {
3945
4354
  getRootProps
3946
4355
  } = _ref2;
3947
4356
  return /*#__PURE__*/react.createElement("div", _extends({}, getRootProps(), {
3948
- className: "m-5 dicom-upload-drop-area-border-dash flex flex-col items-center justify-center h-full"
4357
+ className: "dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
3949
4358
  }), /*#__PURE__*/react.createElement("div", {
3950
4359
  className: "flex gap-3"
3951
4360
  }, /*#__PURE__*/react.createElement(es/* default */.Z, {
@@ -3969,7 +4378,7 @@ function DicomUpload(_ref) {
3969
4378
  getInputProps
3970
4379
  } = _ref4;
3971
4380
  return /*#__PURE__*/react.createElement("div", getRootProps(), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
3972
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary,
4381
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary,
3973
4382
  disabled: false,
3974
4383
  onClick: () => {}
3975
4384
  }, 'Add folder', /*#__PURE__*/react.createElement("input", _extends({}, getInputProps(), {
@@ -3979,7 +4388,7 @@ function DicomUpload(_ref) {
3979
4388
  })), /*#__PURE__*/react.createElement("div", {
3980
4389
  className: "pt-5"
3981
4390
  }, "or drag images or folders here"), /*#__PURE__*/react.createElement("div", {
3982
- className: "pt-3 text-aqua-pale text-lg"
4391
+ className: "text-aqua-pale pt-3 text-lg"
3983
4392
  }, "(DICOM files supported)"));
3984
4393
  });
3985
4394
  };
@@ -4043,7 +4452,7 @@ function getCustomizationModule() {
4043
4452
  }
4044
4453
  /* harmony default export */ const src_getCustomizationModule = (getCustomizationModule);
4045
4454
  // EXTERNAL MODULE: ../../../node_modules/html2canvas/dist/html2canvas.esm.js
4046
- var html2canvas_esm = __webpack_require__(63691);
4455
+ var html2canvas_esm = __webpack_require__(76010);
4047
4456
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
4048
4457
 
4049
4458
 
@@ -4059,10 +4468,10 @@ const VIEWPORT_ID = 'cornerstone-viewport-download-form';
4059
4468
  const CornerstoneViewportDownloadForm = _ref => {
4060
4469
  let {
4061
4470
  onClose,
4062
- activeViewportIndex,
4471
+ activeViewportId: activeViewportIdProp,
4063
4472
  cornerstoneViewportService
4064
4473
  } = _ref;
4065
- const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIndex);
4474
+ const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIdProp);
4066
4475
  const activeViewportElement = enabledElement?.element;
4067
4476
  const activeViewportEnabledElement = (0,esm.getEnabledElement)(activeViewportElement);
4068
4477
  const {
@@ -4181,13 +4590,18 @@ const CornerstoneViewportDownloadForm = _ref => {
4181
4590
  const imageId = viewport.getCurrentImageId();
4182
4591
  const properties = viewport.getProperties();
4183
4592
  downloadViewport.setStack([imageId]).then(() => {
4184
- downloadViewport.setProperties(properties);
4185
- const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
4186
- const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
4187
- resolve({
4188
- width: newWidth,
4189
- height: newHeight
4190
- });
4593
+ try {
4594
+ downloadViewport.setProperties(properties);
4595
+ const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
4596
+ const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
4597
+ resolve({
4598
+ width: newWidth,
4599
+ height: newHeight
4600
+ });
4601
+ } catch (e) {
4602
+ // Happens on clicking the cancel button
4603
+ console.warn('Unable to set properties', e);
4604
+ }
4191
4605
  });
4192
4606
  } else if (downloadViewport instanceof esm.VolumeViewport) {
4193
4607
  const actors = viewport.getActors();
@@ -4264,74 +4678,74 @@ const CornerstoneViewportDownloadForm = _ref => {
4264
4678
  };
4265
4679
  CornerstoneViewportDownloadForm.propTypes = {
4266
4680
  onClose: (prop_types_default()).func,
4267
- activeViewportIndex: (prop_types_default()).number.isRequired
4681
+ activeViewportId: (prop_types_default()).string.isRequired
4268
4682
  };
4269
4683
  /* harmony default export */ const utils_CornerstoneViewportDownloadForm = (CornerstoneViewportDownloadForm);
4270
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/stackSync/calculateViewportRegistrations.ts
4271
-
4272
- function calculateViewportRegistrations(viewports) {
4273
- const viewportPairs = _getViewportPairs(viewports);
4274
- for (const [viewport, nextViewport] of viewportPairs) {
4275
- // check if they are in the same Frame of Reference
4276
- const renderingEngine1 = (0,esm.getRenderingEngine)(viewport.renderingEngineId);
4277
- const renderingEngine2 = (0,esm.getRenderingEngine)(nextViewport.renderingEngineId);
4278
- const csViewport1 = renderingEngine1.getViewport(viewport.viewportId);
4279
- const csViewport2 = renderingEngine2.getViewport(nextViewport.viewportId);
4280
- esm.utilities.calculateViewportsSpatialRegistration(csViewport1, csViewport2);
4281
- }
4282
- }
4283
- const _getViewportPairs = viewports => {
4284
- const viewportPairs = [];
4285
- for (let i = 0; i < viewports.length; i++) {
4286
- for (let j = i + 1; j < viewports.length; j++) {
4287
- viewportPairs.push([viewports[i], viewports[j]]);
4288
- }
4289
- }
4290
- return viewportPairs;
4291
- };
4292
4684
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/stackSync/toggleStackImageSync.ts
4293
-
4294
-
4295
- // [ {
4296
- // synchronizerId: string,
4297
- // viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
4298
- // ]}
4299
- let STACK_IMAGE_SYNC_GROUPS_INFO = [];
4685
+ const STACK_SYNC_NAME = 'stackImageSync';
4300
4686
  function toggleStackImageSync(_ref) {
4301
4687
  let {
4302
4688
  toggledState,
4303
4689
  servicesManager,
4304
- getEnabledElement
4690
+ viewports: providedViewports
4305
4691
  } = _ref;
4692
+ if (!toggledState) {
4693
+ return disableSync(STACK_SYNC_NAME, servicesManager);
4694
+ }
4306
4695
  const {
4307
4696
  syncGroupService,
4308
4697
  viewportGridService,
4309
4698
  displaySetService,
4310
4699
  cornerstoneViewportService
4311
4700
  } = servicesManager.services;
4312
- if (!toggledState) {
4313
- STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
4314
- const {
4315
- viewports,
4316
- synchronizerId
4317
- } = syncGroupInfo;
4318
- viewports.forEach(_ref2 => {
4319
- let {
4320
- viewportId,
4321
- renderingEngineId
4322
- } = _ref2;
4323
- syncGroupService.removeViewportFromSyncGroup(viewportId, renderingEngineId, synchronizerId);
4324
- });
4701
+ const viewports = providedViewports || getReconstructableStackViewports(viewportGridService, displaySetService);
4702
+
4703
+ // create synchronization group and add the viewports to it.
4704
+ viewports.forEach(gridViewport => {
4705
+ const {
4706
+ viewportId
4707
+ } = gridViewport.viewportOptions;
4708
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
4709
+ if (!viewport) {
4710
+ return;
4711
+ }
4712
+ syncGroupService.addViewportToSyncGroup(viewportId, viewport.getRenderingEngine().id, {
4713
+ type: 'stackimage',
4714
+ id: STACK_SYNC_NAME,
4715
+ source: true,
4716
+ target: true
4325
4717
  });
4326
- return;
4327
- }
4328
- STACK_IMAGE_SYNC_GROUPS_INFO = [];
4718
+ });
4719
+ }
4720
+ function disableSync(syncName, servicesManager) {
4721
+ const {
4722
+ syncGroupService,
4723
+ viewportGridService,
4724
+ displaySetService,
4725
+ cornerstoneViewportService
4726
+ } = servicesManager.services;
4727
+ const viewports = getReconstructableStackViewports(viewportGridService, displaySetService);
4728
+ viewports.forEach(gridViewport => {
4729
+ const {
4730
+ viewportId
4731
+ } = gridViewport.viewportOptions;
4732
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
4733
+ if (!viewport) {
4734
+ return;
4735
+ }
4736
+ syncGroupService.removeViewportFromSyncGroup(viewport.id, viewport.getRenderingEngine().id, syncName);
4737
+ });
4738
+ }
4329
4739
 
4330
- // create synchronization groups and add viewports
4740
+ /**
4741
+ * Gets the consistent spacing stack viewport types, which are the ones which
4742
+ * can be navigated using the stack image sync right now.
4743
+ */
4744
+ function getReconstructableStackViewports(viewportGridService, displaySetService) {
4331
4745
  let {
4332
4746
  viewports
4333
4747
  } = viewportGridService.getState();
4334
-
4748
+ viewports = [...viewports.values()];
4335
4749
  // filter empty viewports
4336
4750
  viewports = viewports.filter(viewport => viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length);
4337
4751
 
@@ -4342,103 +4756,16 @@ function toggleStackImageSync(_ref) {
4342
4756
  } = viewport;
4343
4757
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
4344
4758
  const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
4759
+
4760
+ // TODO - add a better test than isReconstructable
4345
4761
  if (displaySet && displaySet.isReconstructable) {
4346
4762
  return true;
4347
4763
  }
4348
4764
  return false;
4349
4765
  }
4350
4766
  });
4351
- const viewportsByOrientation = viewports.reduce((acc, viewport) => {
4352
- const {
4353
- viewportId,
4354
- viewportType
4355
- } = viewport.viewportOptions;
4356
- if (viewportType !== 'stack') {
4357
- console.warn('Viewport is not a stack, cannot sync images yet');
4358
- return acc;
4359
- }
4360
- const {
4361
- element
4362
- } = cornerstoneViewportService.getViewportInfo(viewportId);
4363
- const {
4364
- viewport: csViewport,
4365
- renderingEngineId
4366
- } = getEnabledElement(element);
4367
- const {
4368
- viewPlaneNormal
4369
- } = csViewport.getCamera();
4370
-
4371
- // Should we round here? I guess so, but not sure how much precision we need
4372
- const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
4373
- if (!acc[orientation]) {
4374
- acc[orientation] = [];
4375
- }
4376
- acc[orientation].push({
4377
- viewportId,
4378
- renderingEngineId
4379
- });
4380
- return acc;
4381
- }, {});
4382
-
4383
- // create synchronizer for each group
4384
- Object.values(viewportsByOrientation).map(viewports => {
4385
- let synchronizerId = viewports.map(_ref3 => {
4386
- let {
4387
- viewportId
4388
- } = _ref3;
4389
- return viewportId;
4390
- }).join(',');
4391
- synchronizerId = `imageSync_${synchronizerId}`;
4392
- calculateViewportRegistrations(viewports);
4393
- viewports.forEach(_ref4 => {
4394
- let {
4395
- viewportId,
4396
- renderingEngineId
4397
- } = _ref4;
4398
- syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, {
4399
- type: 'stackimage',
4400
- id: synchronizerId,
4401
- source: true,
4402
- target: true
4403
- });
4404
- });
4405
- STACK_IMAGE_SYNC_GROUPS_INFO.push({
4406
- synchronizerId,
4407
- viewports
4408
- });
4409
- });
4410
- }
4411
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/selection.ts
4412
-
4413
-
4414
- /**
4415
- * Check whether an annotation from imaging library is selected or not.
4416
- * @param {string} annotationUID uid of imaging library annotation
4417
- * @returns boolean
4418
- */
4419
- function isAnnotationSelected(annotationUID) {
4420
- return dist_esm.annotation.selection.isAnnotationSelected(annotationUID);
4421
- }
4422
-
4423
- /**
4424
- * Change an annotation from imaging library's selected property.
4425
- * @param annotationUID - uid of imaging library annotation
4426
- * @param selected - new value for selected
4427
- */
4428
- function setAnnotationSelected(annotationUID, selected) {
4429
- const isCurrentSelected = isAnnotationSelected(annotationUID);
4430
- // branch cut, avoid invoking imaging library unnecessarily.
4431
- if (isCurrentSelected !== selected) {
4432
- dist_esm.annotation.selection.setAnnotationSelected(annotationUID, selected);
4433
- }
4434
- }
4435
- function getFirstAnnotationSelected(element) {
4436
- const [selectedAnnotationUID] = dist_esm.annotation.selection.getAnnotationsSelected() || [];
4437
- if (selectedAnnotationUID) {
4438
- return dist_esm.annotation.state.getAnnotation(selectedAnnotationUID);
4439
- }
4767
+ return viewports;
4440
4768
  }
4441
-
4442
4769
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/commandsModule.ts
4443
4770
 
4444
4771
 
@@ -4583,7 +4910,7 @@ function commandsModule(_ref) {
4583
4910
  * @param props - containing the updates to apply
4584
4911
  * @param props.measurementKey - chooses the measurement key to apply the
4585
4912
  * code to. This will typically be finding or site to apply a
4586
- * finind code or a findingSites code.
4913
+ * finding code or a findingSites code.
4587
4914
  * @param props.code - A coding scheme value from DICOM, including:
4588
4915
  * * CodeValue - the language independent code, for example '1234'
4589
4916
  * * CodingSchemeDesignator - the issue of the code value
@@ -4650,8 +4977,7 @@ function commandsModule(_ref) {
4650
4977
  console.warn('No viewport found for viewportId:', viewportId);
4651
4978
  return;
4652
4979
  }
4653
- const viewportIndex = viewportInfo.getViewportIndex();
4654
- viewportGridService.setActiveViewportIndex(viewportIndex);
4980
+ viewportGridService.setActiveViewportId(viewportId);
4655
4981
  },
4656
4982
  arrowTextCallback: _ref7 => {
4657
4983
  let {
@@ -4660,6 +4986,22 @@ function commandsModule(_ref) {
4660
4986
  } = _ref7;
4661
4987
  utils_callInputDialog(uiDialogService, data, callback);
4662
4988
  },
4989
+ cleanUpCrosshairs: () => {
4990
+ // if the crosshairs tool is active, deactivate it and set window level active
4991
+ // since we are going back to main non-mpr HP
4992
+ const activeViewportToolGroup = toolGroupService.getToolGroup(null);
4993
+ if (activeViewportToolGroup._toolInstances?.Crosshairs?.mode === dist_esm.Enums.ToolModes.Active) {
4994
+ actions.toolbarServiceRecordInteraction({
4995
+ interactionType: 'tool',
4996
+ commands: [{
4997
+ commandOptions: {
4998
+ toolName: 'WindowLevel'
4999
+ },
5000
+ context: 'CORNERSTONE'
5001
+ }]
5002
+ });
5003
+ }
5004
+ },
4663
5005
  toggleCine: () => {
4664
5006
  const {
4665
5007
  viewports
@@ -4716,10 +5058,16 @@ function commandsModule(_ref) {
4716
5058
  toolbarServiceRecordInteraction: props => {
4717
5059
  toolbarService.recordInteraction(props);
4718
5060
  },
5061
+ // Enable or disable a toggleable command, without calling the activation
5062
+ // Used to setup already active tools from hanging protocols
5063
+ setToolbarToggled: props => {
5064
+ toolbarService.setToggled(props.toolId, props.isActive ?? true);
5065
+ },
4719
5066
  setToolActive: _ref9 => {
4720
5067
  let {
4721
5068
  toolName,
4722
- toolGroupId = null
5069
+ toolGroupId = null,
5070
+ toggledState
4723
5071
  } = _ref9;
4724
5072
  if (toolName === 'Crosshairs') {
4725
5073
  const activeViewportToolGroup = toolGroupService.getToolGroup(null);
@@ -4735,23 +5083,12 @@ function commandsModule(_ref) {
4735
5083
  }
4736
5084
  const {
4737
5085
  viewports
4738
- } = viewportGridService.getState() || {
4739
- viewports: []
4740
- };
4741
- const toolGroup = toolGroupService.getToolGroup(toolGroupId);
4742
- const toolGroupViewportIds = toolGroup?.getViewportIds?.();
4743
-
4744
- // if toolGroup has been destroyed, or its viewports have been removed
4745
- if (!toolGroupViewportIds || !toolGroupViewportIds.length) {
5086
+ } = viewportGridService.getState();
5087
+ if (!viewports.size) {
4746
5088
  return;
4747
5089
  }
4748
- const filteredViewports = viewports.filter(viewport => {
4749
- if (!viewport.viewportOptions) {
4750
- return false;
4751
- }
4752
- return toolGroupViewportIds.includes(viewport.viewportOptions.viewportId);
4753
- });
4754
- if (!filteredViewports.length) {
5090
+ const toolGroup = toolGroupService.getToolGroup(toolGroupId);
5091
+ if (!toolGroup) {
4755
5092
  return;
4756
5093
  }
4757
5094
  if (!toolGroup.getToolInstance(toolName)) {
@@ -4773,6 +5110,14 @@ function commandsModule(_ref) {
4773
5110
  toolGroup.setToolPassive(activeToolName);
4774
5111
  }
4775
5112
  }
5113
+
5114
+ // If there is a toggle state, then simply set the enabled/disabled state without
5115
+ // setting the tool active.
5116
+ if (toggledState != null) {
5117
+ toggledState ? toolGroup.setToolEnabled(toolName) : toolGroup.setToolDisabled(toolName);
5118
+ return;
5119
+ }
5120
+
4776
5121
  // Set the new toolName to be active
4777
5122
  toolGroup.setToolActive(toolName, {
4778
5123
  bindings: [{
@@ -4782,9 +5127,9 @@ function commandsModule(_ref) {
4782
5127
  },
4783
5128
  showDownloadViewportModal: () => {
4784
5129
  const {
4785
- activeViewportIndex
5130
+ activeViewportId
4786
5131
  } = viewportGridService.getState();
4787
- if (!cornerstoneViewportService.getCornerstoneViewportByIndex(activeViewportIndex)) {
5132
+ if (!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)) {
4788
5133
  // Cannot download a non-cornerstone viewport (image).
4789
5134
  uiNotificationService.show({
4790
5135
  title: 'Download Image',
@@ -4801,7 +5146,7 @@ function commandsModule(_ref) {
4801
5146
  content: utils_CornerstoneViewportDownloadForm,
4802
5147
  title: 'Download High Quality Image',
4803
5148
  contentProps: {
4804
- activeViewportIndex,
5149
+ activeViewportId,
4805
5150
  onClose: uiModalService.hide,
4806
5151
  cornerstoneViewportService
4807
5152
  }
@@ -4882,15 +5227,13 @@ function commandsModule(_ref) {
4882
5227
  const {
4883
5228
  viewport
4884
5229
  } = enabledElement;
4885
- if (viewport instanceof esm.StackViewport) {
4886
- const {
4887
- invert
4888
- } = viewport.getProperties();
4889
- viewport.setProperties({
4890
- invert: !invert
4891
- });
4892
- viewport.render();
4893
- }
5230
+ const {
5231
+ invert
5232
+ } = viewport.getProperties();
5233
+ viewport.setProperties({
5234
+ invert: !invert
5235
+ });
5236
+ viewport.render();
4894
5237
  },
4895
5238
  resetViewport: () => {
4896
5239
  const enabledElement = _getActiveViewportEnabledElement();
@@ -4904,7 +5247,7 @@ function commandsModule(_ref) {
4904
5247
  viewport.resetProperties();
4905
5248
  viewport.resetCamera();
4906
5249
  } else {
4907
- // Todo: add reset properties for volume viewport
5250
+ viewport.resetProperties();
4908
5251
  viewport.resetCamera();
4909
5252
  }
4910
5253
  viewport.render();
@@ -4993,12 +5336,12 @@ function commandsModule(_ref) {
4993
5336
  },
4994
5337
  setViewportColormap: _ref15 => {
4995
5338
  let {
4996
- viewportIndex,
5339
+ viewportId,
4997
5340
  displaySetInstanceUID,
4998
5341
  colormap,
4999
5342
  immediate = false
5000
5343
  } = _ref15;
5001
- const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(viewportIndex);
5344
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
5002
5345
  const actorEntries = viewport.getActors();
5003
5346
  const actorEntry = actorEntries.find(actorEntry => {
5004
5347
  return actorEntry.uid.includes(displaySetInstanceUID);
@@ -5015,51 +5358,53 @@ function commandsModule(_ref) {
5015
5358
  viewport.render();
5016
5359
  }
5017
5360
  },
5018
- incrementActiveViewport: () => {
5019
- const {
5020
- activeViewportIndex,
5021
- viewports
5022
- } = viewportGridService.getState();
5023
- const nextViewportIndex = (activeViewportIndex + 1) % viewports.length;
5024
- viewportGridService.setActiveViewportIndex(nextViewportIndex);
5025
- },
5026
- decrementActiveViewport: () => {
5361
+ changeActiveViewport: _ref16 => {
5362
+ let {
5363
+ direction = 1
5364
+ } = _ref16;
5027
5365
  const {
5028
- activeViewportIndex,
5366
+ activeViewportId,
5029
5367
  viewports
5030
5368
  } = viewportGridService.getState();
5031
- const nextViewportIndex = (activeViewportIndex - 1 + viewports.length) % viewports.length;
5032
- viewportGridService.setActiveViewportIndex(nextViewportIndex);
5369
+ const viewportIds = Array.from(viewports.keys());
5370
+ const currentIndex = viewportIds.indexOf(activeViewportId);
5371
+ const nextViewportIndex = (currentIndex + direction + viewportIds.length) % viewportIds.length;
5372
+ viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex]);
5033
5373
  },
5034
- toggleStackImageSync: _ref16 => {
5374
+ toggleStackImageSync: _ref17 => {
5035
5375
  let {
5036
5376
  toggledState
5037
- } = _ref16;
5377
+ } = _ref17;
5038
5378
  toggleStackImageSync({
5039
- getEnabledElement: esm.getEnabledElement,
5040
5379
  servicesManager,
5041
5380
  toggledState
5042
- });
5043
- },
5044
- toggleReferenceLines: _ref17 => {
5045
- let {
5046
- toggledState
5047
- } = _ref17;
5048
- const {
5049
- activeViewportIndex
5050
- } = viewportGridService.getState();
5051
- const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(activeViewportIndex);
5052
- const viewportId = viewportInfo.getViewportId();
5053
- const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
5054
- if (!toggledState) {
5055
- toolGroup.setToolDisabled(dist_esm.ReferenceLinesTool.toolName);
5381
+ });
5382
+ },
5383
+ setSourceViewportForReferenceLinesTool: _ref18 => {
5384
+ let {
5385
+ toggledState,
5386
+ viewportId
5387
+ } = _ref18;
5388
+ if (!viewportId) {
5389
+ const {
5390
+ activeViewportId
5391
+ } = viewportGridService.getState();
5392
+ viewportId = activeViewportId;
5056
5393
  }
5394
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
5057
5395
  toolGroup.setToolConfiguration(dist_esm.ReferenceLinesTool.toolName, {
5058
5396
  sourceViewportId: viewportId
5059
5397
  }, true // overwrite
5060
5398
  );
5399
+ },
5061
5400
 
5062
- toolGroup.setToolEnabled(dist_esm.ReferenceLinesTool.toolName);
5401
+ storePresentation: _ref19 => {
5402
+ let {
5403
+ viewportId
5404
+ } = _ref19;
5405
+ cornerstoneViewportService.storePresentation({
5406
+ viewportId
5407
+ });
5063
5408
  }
5064
5409
  };
5065
5410
  const definitions = {
@@ -5114,10 +5459,13 @@ function commandsModule(_ref) {
5114
5459
  }
5115
5460
  },
5116
5461
  incrementActiveViewport: {
5117
- commandFn: actions.incrementActiveViewport
5462
+ commandFn: actions.changeActiveViewport
5118
5463
  },
5119
5464
  decrementActiveViewport: {
5120
- commandFn: actions.decrementActiveViewport
5465
+ commandFn: actions.changeActiveViewport,
5466
+ options: {
5467
+ direction: -1
5468
+ }
5121
5469
  },
5122
5470
  flipViewportHorizontal: {
5123
5471
  commandFn: actions.flipViewportHorizontal
@@ -5194,8 +5542,17 @@ function commandsModule(_ref) {
5194
5542
  toggleStackImageSync: {
5195
5543
  commandFn: actions.toggleStackImageSync
5196
5544
  },
5197
- toggleReferenceLines: {
5198
- commandFn: actions.toggleReferenceLines
5545
+ setSourceViewportForReferenceLinesTool: {
5546
+ commandFn: actions.setSourceViewportForReferenceLinesTool
5547
+ },
5548
+ storePresentation: {
5549
+ commandFn: actions.storePresentation
5550
+ },
5551
+ setToolbarToggled: {
5552
+ commandFn: actions.setToolbarToggled
5553
+ },
5554
+ cleanUpCrosshairs: {
5555
+ commandFn: actions.cleanUpCrosshairs
5199
5556
  }
5200
5557
  };
5201
5558
  return {
@@ -5210,9 +5567,8 @@ const mpr = {
5210
5567
  id: 'mpr',
5211
5568
  name: 'Multi-Planar Reconstruction',
5212
5569
  locked: true,
5213
- hasUpdatedPriorsInformation: false,
5214
5570
  createdDate: '2021-02-23',
5215
- modifiedDate: '2023-04-03',
5571
+ modifiedDate: '2023-08-15',
5216
5572
  availableTo: {},
5217
5573
  editableBy: {},
5218
5574
  // Unknown number of priors referenced - so just match any study
@@ -5230,16 +5586,7 @@ const mpr = {
5230
5586
  }],
5231
5587
  // Turns off crosshairs when switching out of MPR mode
5232
5588
  onProtocolExit: [{
5233
- commandName: 'toolbarServiceRecordInteraction',
5234
- commandOptions: {
5235
- interactionType: 'tool',
5236
- commands: [{
5237
- commandOptions: {
5238
- toolName: 'WindowLevel'
5239
- },
5240
- context: 'CORNERSTONE'
5241
- }]
5242
- }
5589
+ commandName: 'cleanUpCrosshairs'
5243
5590
  }]
5244
5591
  },
5245
5592
  displaySetSelectors: {
@@ -5283,6 +5630,7 @@ const mpr = {
5283
5630
  },
5284
5631
  viewports: [{
5285
5632
  viewportOptions: {
5633
+ viewportId: 'mpr-axial',
5286
5634
  toolGroupId: 'mpr',
5287
5635
  viewportType: 'volume',
5288
5636
  orientation: 'axial',
@@ -5301,6 +5649,7 @@ const mpr = {
5301
5649
  }]
5302
5650
  }, {
5303
5651
  viewportOptions: {
5652
+ viewportId: 'mpr-sagittal',
5304
5653
  toolGroupId: 'mpr',
5305
5654
  viewportType: 'volume',
5306
5655
  orientation: 'sagittal',
@@ -5319,6 +5668,7 @@ const mpr = {
5319
5668
  }]
5320
5669
  }, {
5321
5670
  viewportOptions: {
5671
+ viewportId: 'mpr-coronal',
5322
5672
  toolGroupId: 'mpr',
5323
5673
  viewportType: 'volume',
5324
5674
  orientation: 'coronal',
@@ -5341,7 +5691,6 @@ const mpr = {
5341
5691
  const mprAnd3DVolumeViewport = {
5342
5692
  id: 'mprAnd3DVolumeViewport',
5343
5693
  locked: true,
5344
- hasUpdatedPriorsInformation: false,
5345
5694
  name: 'mpr',
5346
5695
  createdDate: '2023-03-15T10:29:44.894Z',
5347
5696
  modifiedDate: '2023-03-15T10:29:44.894Z',
@@ -5530,9 +5879,9 @@ class ToolGroupService {
5530
5879
  return dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngine.id);
5531
5880
  }
5532
5881
  getActiveToolForViewport(viewportId) {
5533
- const toolGroup = dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId);
5882
+ const toolGroup = this.getToolGroupForViewport(viewportId);
5534
5883
  if (!toolGroup) {
5535
- return null;
5884
+ return;
5536
5885
  }
5537
5886
  return toolGroup.getActivePrimaryMouseButtonTool();
5538
5887
  }
@@ -5595,9 +5944,8 @@ class ToolGroupService {
5595
5944
  this._setToolsMode(toolGroup, tools);
5596
5945
  }
5597
5946
  createToolGroupAndAddTools(toolGroupId, tools) {
5598
- let configs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5599
5947
  const toolGroup = this.createToolGroup(toolGroupId);
5600
- this.addToolsToToolGroup(toolGroupId, tools, configs);
5948
+ this.addToolsToToolGroup(toolGroupId, tools);
5601
5949
  return toolGroup;
5602
5950
  }
5603
5951
 
@@ -5643,30 +5991,6 @@ class ToolGroupService {
5643
5991
  const toolInstance = toolGroup.getToolInstance(toolName);
5644
5992
  toolInstance.configuration = config;
5645
5993
  }
5646
- _getToolNames(toolGroupTools) {
5647
- const toolNames = [];
5648
- if (toolGroupTools.active) {
5649
- toolGroupTools.active.forEach(tool => {
5650
- toolNames.push(tool.toolName);
5651
- });
5652
- }
5653
- if (toolGroupTools.passive) {
5654
- toolGroupTools.passive.forEach(tool => {
5655
- toolNames.push(tool.toolName);
5656
- });
5657
- }
5658
- if (toolGroupTools.enabled) {
5659
- toolGroupTools.enabled.forEach(tool => {
5660
- toolNames.push(tool.toolName);
5661
- });
5662
- }
5663
- if (toolGroupTools.disabled) {
5664
- toolGroupTools.disabled.forEach(tool => {
5665
- toolNames.push(tool.toolName);
5666
- });
5667
- }
5668
- return toolNames;
5669
- }
5670
5994
  _setToolsMode(toolGroup, tools) {
5671
5995
  const {
5672
5996
  active,
@@ -5710,29 +6034,46 @@ class ToolGroupService {
5710
6034
  });
5711
6035
  }
5712
6036
  }
5713
- _addTools(toolGroup, tools, configs) {
5714
- const toolNames = this._getToolNames(tools);
5715
- toolNames.forEach(toolName => {
5716
- // Initialize the toolConfig if no configuration is provided
5717
- const toolConfig = configs[toolName] ?? {};
5718
-
5719
- // if (volumeUID) {
5720
- // toolConfig.volumeUID = volumeUID;
5721
- // }
5722
-
5723
- toolGroup.addTool(toolName, {
5724
- ...toolConfig
6037
+ _addTools(toolGroup, tools) {
6038
+ const addTools = tools => {
6039
+ tools.forEach(_ref5 => {
6040
+ let {
6041
+ toolName,
6042
+ parentTool,
6043
+ configuration
6044
+ } = _ref5;
6045
+ if (parentTool) {
6046
+ toolGroup.addToolInstance(toolName, parentTool, {
6047
+ ...configuration
6048
+ });
6049
+ } else {
6050
+ toolGroup.addTool(toolName, {
6051
+ ...configuration
6052
+ });
6053
+ }
5725
6054
  });
5726
- });
6055
+ };
6056
+ if (tools.active) {
6057
+ addTools(tools.active);
6058
+ }
6059
+ if (tools.passive) {
6060
+ addTools(tools.passive);
6061
+ }
6062
+ if (tools.enabled) {
6063
+ addTools(tools.enabled);
6064
+ }
6065
+ if (tools.disabled) {
6066
+ addTools(tools.disabled);
6067
+ }
5727
6068
  }
5728
6069
  }
5729
6070
  ToolGroupService.REGISTRATION = {
5730
6071
  name: 'toolGroupService',
5731
6072
  altName: 'ToolGroupService',
5732
- create: _ref5 => {
6073
+ create: _ref6 => {
5733
6074
  let {
5734
6075
  servicesManager
5735
- } = _ref5;
6076
+ } = _ref6;
5736
6077
  return new ToolGroupService(servicesManager);
5737
6078
  }
5738
6079
  };
@@ -5789,7 +6130,7 @@ class SyncGroupService {
5789
6130
  * @param type is the type of the synchronizer to create
5790
6131
  * @param creator
5791
6132
  */
5792
- setSynchronizer(type, creator) {
6133
+ addSynchronizerType(type, creator) {
5793
6134
  this.synchronizerCreators[type.toLowerCase()] = creator;
5794
6135
  }
5795
6136
  _getOrCreateSynchronizer(type, id, options) {
@@ -5867,10 +6208,10 @@ SyncGroupService.REGISTRATION = {
5867
6208
 
5868
6209
  /* harmony default export */ const services_SyncGroupService = (SyncGroupService);
5869
6210
  // EXTERNAL MODULE: ../../../node_modules/lodash.clonedeep/index.js
5870
- var lodash_clonedeep = __webpack_require__(71975);
6211
+ var lodash_clonedeep = __webpack_require__(11677);
5871
6212
  var lodash_clonedeep_default = /*#__PURE__*/__webpack_require__.n(lodash_clonedeep);
5872
6213
  // EXTERNAL MODULE: ../../../node_modules/lodash.isequal/index.js
5873
- var lodash_isequal = __webpack_require__(68652);
6214
+ var lodash_isequal = __webpack_require__(10311);
5874
6215
  var lodash_isequal_default = /*#__PURE__*/__webpack_require__.n(lodash_isequal);
5875
6216
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/transitions.ts
5876
6217
  /**
@@ -6026,6 +6367,82 @@ class SegmentationService extends src/* PubSubService */.hC {
6026
6367
  segmentation
6027
6368
  });
6028
6369
  };
6370
+ // Todo: this should not run on the main thread
6371
+ this.calculateCentroids = (segmentationId, segmentIndex) => {
6372
+ const segmentation = this.getSegmentation(segmentationId);
6373
+ const volume = this.getLabelmapVolume(segmentationId);
6374
+ const {
6375
+ dimensions,
6376
+ imageData
6377
+ } = volume;
6378
+ const scalarData = volume.getScalarData();
6379
+ const [dimX, dimY, numFrames] = dimensions;
6380
+ const frameLength = dimX * dimY;
6381
+ const segmentIndices = segmentIndex ? [segmentIndex] : segmentation.segments.filter(segment => segment?.segmentIndex).map(segment => segment.segmentIndex);
6382
+ const segmentIndicesSet = new Set(segmentIndices);
6383
+ const centroids = new Map();
6384
+ for (const index of segmentIndicesSet) {
6385
+ centroids.set(index, {
6386
+ x: 0,
6387
+ y: 0,
6388
+ z: 0,
6389
+ count: 0
6390
+ });
6391
+ }
6392
+ let voxelIndex = 0;
6393
+ for (let frame = 0; frame < numFrames; frame++) {
6394
+ for (let p = 0; p < frameLength; p++) {
6395
+ const segmentIndex = scalarData[voxelIndex++];
6396
+ if (segmentIndicesSet.has(segmentIndex)) {
6397
+ const centroid = centroids.get(segmentIndex);
6398
+ centroid.x += p % dimX;
6399
+ centroid.y += p / dimX | 0;
6400
+ centroid.z += frame;
6401
+ centroid.count++;
6402
+ }
6403
+ }
6404
+ }
6405
+ const result = new Map();
6406
+ for (const [index, centroid] of centroids) {
6407
+ const count = centroid.count;
6408
+ const normalizedCentroid = {
6409
+ x: centroid.x / count,
6410
+ y: centroid.y / count,
6411
+ z: centroid.z / count
6412
+ };
6413
+ normalizedCentroid.world = imageData.indexToWorld([normalizedCentroid.x, normalizedCentroid.y, normalizedCentroid.z]);
6414
+ result.set(index, normalizedCentroid);
6415
+ }
6416
+ this.setCentroids(segmentationId, result);
6417
+ return result;
6418
+ };
6419
+ this.setCentroids = (segmentationId, centroids) => {
6420
+ const segmentation = this.getSegmentation(segmentationId);
6421
+ const imageData = this.getLabelmapVolume(segmentationId).imageData; // Assuming this method returns imageData
6422
+
6423
+ if (!segmentation.cachedStats) {
6424
+ segmentation.cachedStats = {
6425
+ segmentCenter: {}
6426
+ };
6427
+ } else if (!segmentation.cachedStats.segmentCenter) {
6428
+ segmentation.cachedStats.segmentCenter = {};
6429
+ }
6430
+ for (const [segmentIndex, centroid] of centroids) {
6431
+ let world = centroid.world;
6432
+
6433
+ // If world coordinates are not provided, calculate them
6434
+ if (!world || world.length === 0) {
6435
+ world = imageData.indexToWorld(centroid.image);
6436
+ }
6437
+ segmentation.cachedStats.segmentCenter[segmentIndex] = {
6438
+ center: {
6439
+ image: centroid.image,
6440
+ world: world
6441
+ }
6442
+ };
6443
+ }
6444
+ this.addOrUpdateSegmentation(segmentation, true, true);
6445
+ };
6029
6446
  this.createSegmentationForDisplaySet = async (displaySetInstanceUID, options) => {
6030
6447
  const {
6031
6448
  displaySetService
@@ -6043,7 +6460,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6043
6460
  volumeId: segmentationId,
6044
6461
  targetBuffer: {
6045
6462
  type: 'Uint8Array',
6046
- sharedArrayBuffer: true
6463
+ sharedArrayBuffer: window.SharedArrayBuffer
6047
6464
  }
6048
6465
  });
6049
6466
  const defaultScheme = this._getDefaultSegmentationScheme();
@@ -6055,6 +6472,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6055
6472
  // We should set it as active by default, as it created for display
6056
6473
  isActive: true,
6057
6474
  type: representationType,
6475
+ FrameOfReferenceUID: options?.FrameOfReferenceUID || displaySet.instances?.[0]?.FrameOfReferenceUID,
6058
6476
  representationData: {
6059
6477
  LABELMAP: {
6060
6478
  volumeId: segmentationId,
@@ -6078,6 +6496,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6078
6496
  this.addSegmentationRepresentationToToolGroup = async function (toolGroupId, segmentationId) {
6079
6497
  let hydrateSegmentation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
6080
6498
  let representationType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : dist_esm.Enums.SegmentationRepresentations.Labelmap;
6499
+ let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6081
6500
  const segmentation = _this.getSegmentation(segmentationId);
6082
6501
  if (!segmentation) {
6083
6502
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
@@ -6122,12 +6541,17 @@ class SegmentationService extends src/* PubSubService */.hC {
6122
6541
  if (visibility !== undefined) {
6123
6542
  _this._setSegmentVisibility(segmentationId, segmentIndex, visibility, toolGroupId, suppressEvents);
6124
6543
  }
6125
- if (isLocked !== undefined) {
6544
+ if (isLocked) {
6126
6545
  _this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6127
6546
  }
6128
6547
  }
6548
+ if (!suppressEvents) {
6549
+ _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6550
+ segmentation
6551
+ });
6552
+ }
6129
6553
  };
6130
- this.setSegmentRGBAColorForSegmentation = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6554
+ this.setSegmentRGBAColor = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6131
6555
  const segmentation = this.getSegmentation(segmentationId);
6132
6556
  if (segmentation === undefined) {
6133
6557
  throw new Error(`no segmentation for segmentationId: ${segmentationId}`);
@@ -6153,6 +6577,9 @@ class SegmentationService extends src/* PubSubService */.hC {
6153
6577
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
6154
6578
  }
6155
6579
  segmentation.hydrated = true;
6580
+
6581
+ // Not all segmentations have dipslaysets, some of them are derived in the client
6582
+ _this._setDisplaySetIsHydrated(segmentationId, true);
6156
6583
  if (!suppressEvents) {
6157
6584
  _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6158
6585
  segmentation
@@ -6160,7 +6587,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6160
6587
  }
6161
6588
  };
6162
6589
  this.getConfiguration = toolGroupId => {
6163
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
6590
+ toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
6164
6591
  const brushSize = 1;
6165
6592
  // const brushSize = cstUtils.segmentation.getBrushSizeForToolGroup(
6166
6593
  // toolGroupId
@@ -6287,7 +6714,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6287
6714
  if (segmentInfo === undefined) {
6288
6715
  throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
6289
6716
  }
6290
- toolGroupId = toolGroupId ?? _this._getFirstToolGroupId();
6717
+ toolGroupId = toolGroupId ?? _this._getApplicableToolGroupId();
6291
6718
  const segmentationRepresentation = _this._getSegmentationRepresentation(segmentationId, toolGroupId);
6292
6719
  if (!segmentationRepresentation) {
6293
6720
  throw new Error('Must add representation to toolgroup before setting segments');
@@ -6314,7 +6741,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6314
6741
  if (segmentInfo === undefined) {
6315
6742
  throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
6316
6743
  }
6317
- toolGroupId = toolGroupId ?? _this._getFirstToolGroupId();
6744
+ toolGroupId = toolGroupId ?? _this._getApplicableToolGroupId();
6318
6745
  const segmentationRepresentation = _this._getSegmentationRepresentation(segmentationId, toolGroupId);
6319
6746
  if (!segmentationRepresentation) {
6320
6747
  throw new Error('Must add representation to toolgroup before setting segments');
@@ -6436,12 +6863,18 @@ class SegmentationService extends src/* PubSubService */.hC {
6436
6863
  });
6437
6864
  });
6438
6865
  };
6439
- this._getFirstToolGroupId = () => {
6866
+ this._getApplicableToolGroupId = () => {
6440
6867
  const {
6441
- toolGroupService
6868
+ toolGroupService,
6869
+ viewportGridService,
6870
+ cornerstoneViewportService
6442
6871
  } = this.servicesManager.services;
6443
- const toolGroupIds = toolGroupService.getToolGroupIds();
6444
- return toolGroupIds[0];
6872
+ const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportGridService.getActiveViewportId());
6873
+ if (!viewportInfo) {
6874
+ const toolGroupIds = toolGroupService.getToolGroupIds();
6875
+ return toolGroupIds[0];
6876
+ }
6877
+ return viewportInfo.getToolGroupId();
6445
6878
  };
6446
6879
  this.getNextColorLUTIndex = () => {
6447
6880
  let i = 0;
@@ -6467,34 +6900,40 @@ class SegmentationService extends src/* PubSubService */.hC {
6467
6900
  this._initSegmentationService();
6468
6901
  }
6469
6902
  /**
6470
- * It adds a segment to a segmentation, basically just setting the properties for
6471
- * the segment.
6472
- * @param segmentationId - The ID of the segmentation you want to add a
6473
- * segment to.
6474
- * @param segmentIndex - The index of the segment to add.
6475
- * @param properties - The properties of the segment to add including
6476
- * -- label: the label of the segment
6477
- * -- color: the color of the segment
6478
- * -- opacity: the opacity of the segment
6479
- * -- visibility: the visibility of the segment (boolean)
6480
- * -- isLocked: whether the segment is locked for editing
6481
- * -- active: whether the segment is currently the active segment to be edited
6903
+ * Adds a new segment to the specified segmentation.
6904
+ * @param segmentationId - The ID of the segmentation to add the segment to.
6905
+ * @param config - An object containing the configuration options for the new segment.
6906
+ * - segmentIndex: (optional) The index of the segment to add. If not provided, the next available index will be used.
6907
+ * - toolGroupId: (optional) The ID of the tool group to associate the new segment with. If not provided, the first available tool group will be used.
6908
+ * - properties: (optional) An object containing the properties of the new segment.
6909
+ * - label: (optional) The label of the new segment. If not provided, a default label will be used.
6910
+ * - color: (optional) The color of the new segment in RGB format. If not provided, a default color will be used.
6911
+ * - opacity: (optional) The opacity of the new segment. If not provided, a default opacity will be used.
6912
+ * - visibility: (optional) Whether the new segment should be visible. If not provided, the segment will be visible by default.
6913
+ * - isLocked: (optional) Whether the new segment should be locked for editing. If not provided, the segment will not be locked by default.
6914
+ * - active: (optional) Whether the new segment should be the active segment to be edited. If not provided, the segment will not be active by default.
6482
6915
  */
6483
- addSegment(segmentationId, segmentIndex, toolGroupId, properties) {
6484
- if (segmentIndex === 0) {
6916
+ addSegment(segmentationId) {
6917
+ let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6918
+ if (config?.segmentIndex === 0) {
6485
6919
  throw new Error('Segment index 0 is reserved for "no label"');
6486
6920
  }
6487
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
6921
+ const toolGroupId = config.toolGroupId ?? this._getApplicableToolGroupId();
6488
6922
  const {
6489
6923
  segmentationRepresentationUID,
6490
6924
  segmentation
6491
6925
  } = this._getSegmentationInfo(segmentationId, toolGroupId);
6926
+ let segmentIndex = config.segmentIndex;
6927
+ if (!segmentIndex) {
6928
+ // grab the next available segment index
6929
+ segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
6930
+ }
6492
6931
  if (this._getSegmentInfo(segmentation, segmentIndex)) {
6493
6932
  throw new Error(`Segment ${segmentIndex} already exists`);
6494
6933
  }
6495
6934
  const rgbaColor = dist_esm.segmentation.config.color.getColorForSegmentIndex(toolGroupId, segmentationRepresentationUID, segmentIndex);
6496
6935
  segmentation.segments[segmentIndex] = {
6497
- label: properties.label,
6936
+ label: config.properties?.label ?? `Segment ${segmentIndex}`,
6498
6937
  segmentIndex: segmentIndex,
6499
6938
  color: [rgbaColor[0], rgbaColor[1], rgbaColor[2]],
6500
6939
  opacity: rgbaColor[3],
@@ -6502,15 +6941,18 @@ class SegmentationService extends src/* PubSubService */.hC {
6502
6941
  isLocked: false
6503
6942
  };
6504
6943
  segmentation.segmentCount++;
6944
+
6945
+ // make the newly added segment the active segment
6946
+ this._setActiveSegment(segmentationId, segmentIndex);
6505
6947
  const suppressEvents = true;
6506
- if (properties !== undefined) {
6948
+ if (config.properties !== undefined) {
6507
6949
  const {
6508
6950
  color: newColor,
6509
6951
  opacity,
6510
6952
  isLocked,
6511
6953
  visibility,
6512
6954
  active
6513
- } = properties;
6955
+ } = config.properties;
6514
6956
  if (newColor !== undefined) {
6515
6957
  this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
6516
6958
  }
@@ -6520,7 +6962,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6520
6962
  if (visibility !== undefined) {
6521
6963
  this._setSegmentVisibility(segmentationId, segmentIndex, visibility, toolGroupId, suppressEvents);
6522
6964
  }
6523
- if (active !== undefined) {
6965
+ if (active === true) {
6524
6966
  this._setActiveSegment(segmentationId, segmentIndex, suppressEvents);
6525
6967
  }
6526
6968
  if (isLocked !== undefined) {
@@ -6590,12 +7032,21 @@ class SegmentationService extends src/* PubSubService */.hC {
6590
7032
  let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6591
7033
  this._setSegmentVisibility(segmentationId, segmentIndex, isVisible, toolGroupId, suppressEvents);
6592
7034
  }
6593
- setSegmentLockedForSegmentation(segmentationId, segmentIndex, isLocked) {
7035
+ setSegmentLocked(segmentationId, segmentIndex, isLocked) {
6594
7036
  const suppressEvents = false;
6595
7037
  this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6596
7038
  }
6597
- setSegmentLabel(segmentationId, segmentIndex, segmentLabel) {
6598
- this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
7039
+
7040
+ /**
7041
+ * Toggles the locked state of a segment in a segmentation.
7042
+ * @param segmentationId - The ID of the segmentation.
7043
+ * @param segmentIndex - The index of the segment to toggle.
7044
+ */
7045
+ toggleSegmentLocked(segmentationId, segmentIndex) {
7046
+ const segmentation = this.getSegmentation(segmentationId);
7047
+ const segment = this._getSegmentInfo(segmentation, segmentIndex);
7048
+ const isLocked = !segment.isLocked;
7049
+ this._setSegmentLocked(segmentationId, segmentIndex, isLocked);
6599
7050
  }
6600
7051
  setSegmentColor(segmentationId, segmentIndex, color, toolGroupId) {
6601
7052
  this._setSegmentColor(segmentationId, segmentIndex, color, toolGroupId);
@@ -6604,11 +7055,11 @@ class SegmentationService extends src/* PubSubService */.hC {
6604
7055
  this._setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId);
6605
7056
  }
6606
7057
  setActiveSegmentationForToolGroup(segmentationId, toolGroupId) {
6607
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
7058
+ toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
6608
7059
  const suppressEvents = false;
6609
7060
  this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
6610
7061
  }
6611
- setActiveSegmentForSegmentation(segmentationId, segmentIndex) {
7062
+ setActiveSegment(segmentationId, segmentIndex) {
6612
7063
  this._setActiveSegment(segmentationId, segmentIndex, false);
6613
7064
  }
6614
7065
 
@@ -6634,6 +7085,21 @@ class SegmentationService extends src/* PubSubService */.hC {
6634
7085
  const segmentations = this.arrayOfObjects(this.segmentations);
6635
7086
  return segmentations && segmentations.map(m => this.segmentations[Object.keys(m)[0]]);
6636
7087
  }
7088
+ getActiveSegmentation() {
7089
+ const segmentations = this.getSegmentations();
7090
+ return segmentations.find(segmentation => segmentation.isActive);
7091
+ }
7092
+ getActiveSegment() {
7093
+ const activeSegmentation = this.getActiveSegmentation();
7094
+ const {
7095
+ activeSegmentIndex,
7096
+ segments
7097
+ } = activeSegmentation;
7098
+ if (activeSegmentIndex === null) {
7099
+ return;
7100
+ }
7101
+ return segments[activeSegmentIndex];
7102
+ }
6637
7103
 
6638
7104
  /**
6639
7105
  * Get specific segmentation by its id.
@@ -6677,11 +7143,14 @@ class SegmentationService extends src/* PubSubService */.hC {
6677
7143
  }
6678
7144
  }]);
6679
7145
 
6680
- // Define a new color LUT and associate it with this segmentation.
6681
- // Todo: need to be generalized to accept custom color LUTs
6682
- const newColorLUT = this.generateNewColorLUT();
6683
- const newColorLUTIndex = this.getNextColorLUTIndex();
6684
- dist_esm.segmentation.config.color.addColorLUT(newColorLUT, newColorLUTIndex);
7146
+ // if first segmentation, we can use the default colorLUT, otherwise
7147
+ // we need to generate a new one and use a new colorLUT
7148
+ const colorLUTIndex = 0;
7149
+ if (Object.keys(this.segmentations).length !== 0) {
7150
+ const newColorLUT = this.generateNewColorLUT();
7151
+ const colorLUTIndex = this.getNextColorLUTIndex();
7152
+ dist_esm.segmentation.config.color.addColorLUT(newColorLUT, colorLUTIndex);
7153
+ }
6685
7154
  this.segmentations[segmentationId] = {
6686
7155
  ...segmentation,
6687
7156
  label: segmentation.label || '',
@@ -6689,8 +7158,8 @@ class SegmentationService extends src/* PubSubService */.hC {
6689
7158
  activeSegmentIndex: segmentation.activeSegmentIndex ?? null,
6690
7159
  segmentCount: segmentation.segmentCount ?? 0,
6691
7160
  isActive: false,
6692
- colorLUTIndex: newColorLUTIndex,
6693
- isVisible: true
7161
+ isVisible: true,
7162
+ colorLUTIndex
6694
7163
  };
6695
7164
  cachedSegmentation = this.segmentations[segmentationId];
6696
7165
  this._updateCornerstoneSegmentations({
@@ -6715,6 +7184,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6715
7184
  id: segmentationId,
6716
7185
  displaySetInstanceUID: segDisplaySet.displaySetInstanceUID,
6717
7186
  type: representationType,
7187
+ label: segDisplaySet.SeriesDescription,
6718
7188
  representationData: {
6719
7189
  [LABELMAP]: {
6720
7190
  volumeId: segmentationId,
@@ -6731,11 +7201,11 @@ class SegmentationService extends src/* PubSubService */.hC {
6731
7201
  return this.addOrUpdateSegmentation(Object.assign(segmentation, cachedSegmentation), suppressEvents);
6732
7202
  }
6733
7203
  const {
6734
- segments,
7204
+ labelmapBufferArray,
6735
7205
  referencedVolumeId
6736
7206
  } = segDisplaySet;
6737
- if (!segments || !referencedVolumeId) {
6738
- throw new Error('To create the segmentation from SEG displaySet, the displaySet should be loaded first, you can perform segDisplaySet.load() before calling this method.');
7207
+ if (!labelmapBufferArray || !referencedVolumeId) {
7208
+ throw new Error('No labelmapBufferArray or referencedVolumeId found for the SEG displaySet');
6739
7209
  }
6740
7210
 
6741
7211
  // if the labelmap doesn't exist, we need to create it first from the
@@ -6751,75 +7221,30 @@ class SegmentationService extends src/* PubSubService */.hC {
6751
7221
  volumeId: segmentationId,
6752
7222
  targetBuffer: {
6753
7223
  type: 'Uint8Array',
6754
- sharedArrayBuffer: true
7224
+ sharedArrayBuffer: window.SharedArrayBuffer
6755
7225
  }
6756
7226
  });
6757
- const [rows, columns] = derivedVolume.dimensions;
6758
7227
  const derivedVolumeScalarData = derivedVolume.getScalarData();
6759
- const {
6760
- imageIds
6761
- } = referencedVolume;
6762
- const sopUIDImageIdIndexMap = imageIds.reduce((acc, imageId, index) => {
6763
- const {
6764
- sopInstanceUid
6765
- } = esm.metaData.get('generalImageModule', imageId);
6766
- acc[sopInstanceUid] = index;
6767
- return acc;
6768
- }, {});
6769
- const numSegments = Object.keys(segments).length;
6770
- // Note: ideally we could use the TypedArray set method, but since each
6771
- // slice can have multiple segments, we need to loop over each slice and
6772
- // set the segment value for each segment.
6773
- let overlappingSegments = false;
6774
- const _segmentInfoUpdate = (segmentInfo, segmentIndex) => {
7228
+ const segmentsInfo = segDisplaySet.segMetadata.data;
7229
+ derivedVolumeScalarData.set(new Uint8Array(labelmapBufferArray[0]));
7230
+ segmentation.segments = segmentsInfo.map((segmentInfo, segmentIndex) => {
7231
+ if (segmentIndex === 0) {
7232
+ return;
7233
+ }
6775
7234
  const {
6776
- pixelData: segPixelData
7235
+ SegmentedPropertyCategoryCodeSequence,
7236
+ SegmentNumber,
7237
+ SegmentLabel,
7238
+ SegmentAlgorithmType,
7239
+ SegmentAlgorithmName,
7240
+ SegmentedPropertyTypeCodeSequence,
7241
+ rgba
6777
7242
  } = segmentInfo;
6778
- let segmentX = 0;
6779
- let segmentY = 0;
6780
- let segmentZ = 0;
6781
- let count = 0;
6782
- for (const [functionalGroupIndex, functionalGroup] of segmentInfo.functionalGroups.entries()) {
6783
- const {
6784
- ReferencedSOPInstanceUID
6785
- } = functionalGroup.DerivationImageSequence.SourceImageSequence;
6786
- const imageIdIndex = sopUIDImageIdIndexMap[ReferencedSOPInstanceUID];
6787
- if (imageIdIndex === -1) {
6788
- return;
6789
- }
6790
- const step = rows * columns;
6791
-
6792
- // we need a faster way to get the pixel data for the current
6793
- // functional group, which we use typed array view
6794
-
6795
- const functionGroupPixelData = new Uint8Array(segPixelData.buffer, functionalGroupIndex * step, step);
6796
- const functionalGroupStartIndex = imageIdIndex * step;
6797
- const functionalGroupEndIndex = (imageIdIndex + 1) * step;
6798
-
6799
- // Note: this for loop is not optimized, since DICOM SEG stores
6800
- // each segment as a separate labelmap so if there is a slice
6801
- // that has multiple segments, we will have to loop over each
6802
- // segment and we cannot use the TypedArray set method.
6803
- for (let i = functionalGroupStartIndex, j = 0; i < functionalGroupEndIndex; i++, j++) {
6804
- if (functionGroupPixelData[j] !== 0) {
6805
- if (derivedVolumeScalarData[i] !== 0) {
6806
- overlappingSegments = true;
6807
- }
6808
- derivedVolumeScalarData[i] = segmentIndex;
6809
-
6810
- // centroid calculations
6811
- segmentX += i % columns;
6812
- segmentY += Math.floor(i / columns) % rows;
6813
- segmentZ += Math.floor(i / (columns * rows));
6814
- count++;
6815
- }
6816
- }
6817
- }
6818
-
6819
- // centroid calculations
6820
- const x = Math.floor(segmentX / count);
6821
- const y = Math.floor(segmentY / count);
6822
- const z = Math.floor(segmentZ / count);
7243
+ const {
7244
+ x,
7245
+ y,
7246
+ z
7247
+ } = segDisplaySet.centroids.get(segmentIndex);
6823
7248
  const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
6824
7249
  segmentation.cachedStats = {
6825
7250
  ...segmentation.cachedStats,
@@ -6834,51 +7259,24 @@ class SegmentationService extends src/* PubSubService */.hC {
6834
7259
  }
6835
7260
  }
6836
7261
  };
6837
- const numInitialized = Object.keys(segmentation.cachedStats.segmentCenter).length;
6838
-
6839
- // Calculate percentage completed
6840
- const percentComplete = Math.round(numInitialized / numSegments * 100);
6841
- this._broadcastEvent(SegmentationService_EVENTS.SEGMENT_LOADING_COMPLETE, {
6842
- percentComplete,
6843
- numSegments: numSegments
6844
- });
6845
- };
6846
- const promiseArray = [];
6847
- for (const segmentIndex in segments) {
6848
- const segmentInfo = segments[segmentIndex];
6849
-
6850
- // Important: we need a non-blocking way to update the segmentation
6851
- // state, otherwise the UI will freeze and the user will not be able
6852
- // to interact with the app or progress bars will not be updated.
6853
- const promise = new Promise((resolve, reject) => {
6854
- setTimeout(() => {
6855
- _segmentInfoUpdate(segmentInfo, segmentIndex);
6856
- resolve();
6857
- }, 0);
6858
- });
6859
- promiseArray.push(promise);
6860
- }
6861
- await Promise.all(promiseArray);
6862
- segmentation.segmentCount = Object.keys(segments).length;
6863
- segmentation.segments = [null]; // segment 0
6864
-
6865
- Object.keys(segments).forEach(segmentIndex => {
6866
- const segmentInfo = segments[segmentIndex];
6867
- const segIndex = Number(segmentIndex);
6868
- segmentation.segments[segIndex] = {
6869
- label: segmentInfo.label || `Segment ${segIndex}`,
6870
- segmentIndex: Number(segmentIndex),
6871
- color: [segmentInfo.color[0], segmentInfo.color[1], segmentInfo.color[2]],
6872
- opacity: segmentInfo.color[3],
7262
+ return {
7263
+ label: SegmentLabel || `Segment ${SegmentNumber}`,
7264
+ segmentIndex: Number(SegmentNumber),
7265
+ category: SegmentedPropertyCategoryCodeSequence ? SegmentedPropertyCategoryCodeSequence.CodeMeaning : '',
7266
+ type: SegmentedPropertyTypeCodeSequence ? SegmentedPropertyTypeCodeSequence.CodeMeaning : '',
7267
+ algorithmType: SegmentAlgorithmType,
7268
+ algorithmName: SegmentAlgorithmName,
7269
+ color: rgba,
7270
+ opacity: 255,
6873
7271
  isVisible: true,
6874
7272
  isLocked: false
6875
7273
  };
6876
7274
  });
7275
+ segmentation.segmentCount = segmentsInfo.length - 1;
6877
7276
  segDisplaySet.isLoaded = true;
6878
7277
  this._broadcastEvent(SegmentationService_EVENTS.SEGMENTATION_LOADING_COMPLETE, {
6879
7278
  segmentationId,
6880
- segDisplaySet,
6881
- overlappingSegments
7279
+ segDisplaySet
6882
7280
  });
6883
7281
  return this.addOrUpdateSegmentation(segmentation, suppressEvents);
6884
7282
  }
@@ -6911,6 +7309,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6911
7309
  id: segmentationId,
6912
7310
  displaySetInstanceUID: rtDisplaySetUID,
6913
7311
  type: representationType,
7312
+ label: rtDisplaySet.SeriesDescription,
6914
7313
  representationData: {
6915
7314
  [CONTOUR]: {
6916
7315
  geometryIds
@@ -7015,6 +7414,9 @@ class SegmentationService extends src/* PubSubService */.hC {
7015
7414
  toolGroupService
7016
7415
  } = this.servicesManager.services;
7017
7416
  const center = this._getSegmentCenter(segmentationId, segmentIndex);
7417
+ if (!center?.world) {
7418
+ return;
7419
+ }
7018
7420
  const {
7019
7421
  world
7020
7422
  } = center;
@@ -7056,7 +7458,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7056
7458
  clearInterval(this.highlightIntervalId);
7057
7459
  }
7058
7460
  const segmentation = this.getSegmentation(segmentationId);
7059
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
7461
+ toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
7060
7462
  const segmentationRepresentation = this._getSegmentationRepresentation(segmentationId, toolGroupId);
7061
7463
  const {
7062
7464
  type
@@ -7068,6 +7470,17 @@ class SegmentationService extends src/* PubSubService */.hC {
7068
7470
  const adjustedAlpha = type === LABELMAP ? alpha : 1 - alpha;
7069
7471
  highlightFn(segmentIndex, adjustedAlpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation);
7070
7472
  }
7473
+ _setDisplaySetIsHydrated(displaySetUID, isHydrated) {
7474
+ const {
7475
+ displaySetService
7476
+ } = this.servicesManager.services;
7477
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
7478
+ if (!displaySet) {
7479
+ return;
7480
+ }
7481
+ displaySet.isHydrated = isHydrated;
7482
+ displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
7483
+ }
7071
7484
  _highlightLabelmap(segmentIndex, alpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation) {
7072
7485
  const newSegmentSpecificConfig = {
7073
7486
  [segmentIndex]: {
@@ -7170,21 +7583,23 @@ class SegmentationService extends src/* PubSubService */.hC {
7170
7583
 
7171
7584
  if (wasActive) {
7172
7585
  const remainingSegmentations = this._getSegmentations();
7173
- if (remainingSegmentations.length) {
7586
+ const remainingHydratedSegmentations = remainingSegmentations.filter(segmentation => segmentation.hydrated);
7587
+ if (remainingHydratedSegmentations.length) {
7174
7588
  const {
7175
7589
  id
7176
- } = remainingSegmentations[0];
7177
- this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
7590
+ } = remainingHydratedSegmentations[0];
7591
+ this._setActiveSegmentationForToolGroup(id, this._getApplicableToolGroupId(), false);
7178
7592
  }
7179
7593
  }
7594
+ this._setDisplaySetIsHydrated(segmentationId, false);
7180
7595
  this._broadcastEvent(this.EVENTS.SEGMENTATION_REMOVED, {
7181
7596
  segmentationId
7182
7597
  });
7183
7598
  }
7184
- setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7185
- this._setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
7599
+ setSegmentLabel(segmentationId, segmentIndex, label) {
7600
+ this._setSegmentLabel(segmentationId, segmentIndex, label);
7186
7601
  }
7187
- _setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7602
+ _setSegmentLabel(segmentationId, segmentIndex, label) {
7188
7603
  let suppressEvents = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
7189
7604
  const segmentation = this.getSegmentation(segmentationId);
7190
7605
  if (segmentation === undefined) {
@@ -7202,16 +7617,14 @@ class SegmentationService extends src/* PubSubService */.hC {
7202
7617
  });
7203
7618
  }
7204
7619
  }
7205
- shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
7206
- if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
7620
+ shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segmentationFrameOfReferenceUID) {
7621
+ if (!viewportDisplaySetInstanceUIDs?.length) {
7207
7622
  return false;
7208
7623
  }
7209
7624
  const {
7210
7625
  displaySetService
7211
7626
  } = this.servicesManager.services;
7212
7627
  let shouldDisplaySeg = false;
7213
- const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
7214
- const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
7215
7628
 
7216
7629
  // check if the displaySet is sharing the same frameOfReferenceUID
7217
7630
  // with the new segmentation
@@ -7220,7 +7633,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7220
7633
 
7221
7634
  // Todo: this might not be ideal for use cases such as 4D, since we
7222
7635
  // don't want to show the segmentation for all the frames
7223
- if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segFrameOfReferenceUID) {
7636
+ if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segmentationFrameOfReferenceUID) {
7224
7637
  shouldDisplaySeg = true;
7225
7638
  break;
7226
7639
  }
@@ -7330,7 +7743,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7330
7743
  }
7331
7744
  _setSegmentVisibility(segmentationId, segmentIndex, isVisible, toolGroupId) {
7332
7745
  let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
7333
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
7746
+ toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
7334
7747
  const {
7335
7748
  segmentationRepresentationUID,
7336
7749
  segmentation
@@ -7374,7 +7787,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7374
7787
  }
7375
7788
  _getSegmentationRepresentation(segmentationId, toolGroupId) {
7376
7789
  const segmentationRepresentations = this.getSegmentationRepresentationsForToolGroup(toolGroupId);
7377
- if (segmentationRepresentations.length === 0) {
7790
+ if (!segmentationRepresentations?.length) {
7378
7791
  return;
7379
7792
  }
7380
7793
 
@@ -7659,7 +8072,8 @@ class CornerstoneCacheService {
7659
8072
  }
7660
8073
  _shouldRenderSegmentation(displaySets) {
7661
8074
  const {
7662
- segmentationService
8075
+ segmentationService,
8076
+ displaySetService
7663
8077
  } = this.servicesManager.services;
7664
8078
  const viewportDisplaySetInstanceUIDs = displaySets.map(_ref2 => {
7665
8079
  let {
@@ -7673,7 +8087,9 @@ class CornerstoneCacheService {
7673
8087
  const segmentations = segmentationService.getSegmentations();
7674
8088
  for (const segmentation of segmentations) {
7675
8089
  const segDisplaySetInstanceUID = segmentation.displaySetInstanceUID;
7676
- const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID);
8090
+ const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
8091
+ const instance = segDisplaySet.instances?.[0] || segDisplaySet.instance;
8092
+ const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, instance.FrameOfReferenceUID);
7677
8093
  if (shouldDisplaySeg) {
7678
8094
  return true;
7679
8095
  }
@@ -7747,16 +8163,17 @@ const DEFAULT_TOOLGROUP_ID = 'default';
7747
8163
  // Return true if the data contains the given display set UID OR the imageId
7748
8164
  // if it is a composite object.
7749
8165
  const dataContains = (data, displaySetUID, imageId) => {
7750
- if (data.displaySetInstanceUID === displaySetUID) return true;
8166
+ if (data.displaySetInstanceUID === displaySetUID) {
8167
+ return true;
8168
+ }
7751
8169
  if (imageId && data.isCompositeStack && data.imageIds) {
7752
8170
  return !!data.imageIds.find(dataId => dataId === imageId);
7753
8171
  }
7754
8172
  return false;
7755
8173
  };
7756
8174
  class ViewportInfo {
7757
- constructor(viewportIndex, viewportId) {
8175
+ constructor(viewportId) {
7758
8176
  this.viewportId = '';
7759
- this.viewportIndex = void 0;
7760
8177
  this.element = void 0;
7761
8178
  this.viewportOptions = void 0;
7762
8179
  this.displaySetOptions = void 0;
@@ -7768,7 +8185,6 @@ class ViewportInfo {
7768
8185
  this.viewportOptions = null;
7769
8186
  this.displaySetOptions = null;
7770
8187
  };
7771
- this.viewportIndex = viewportIndex;
7772
8188
  this.viewportId = viewportId;
7773
8189
  this.setPublicViewportOptions({});
7774
8190
  this.setPublicDisplaySetOptions([{}]);
@@ -7779,7 +8195,9 @@ class ViewportInfo {
7779
8195
  * OR if it is a composite stack and contains the given imageId
7780
8196
  */
7781
8197
  contains(displaySetUID, imageId) {
7782
- if (!this.viewportData?.data) return false;
8198
+ if (!this.viewportData?.data) {
8199
+ return false;
8200
+ }
7783
8201
  if (this.viewportData.data.length) {
7784
8202
  return !!this.viewportData.data.find(data => dataContains(data, displaySetUID, imageId));
7785
8203
  }
@@ -7794,9 +8212,6 @@ class ViewportInfo {
7794
8212
  setViewportId(viewportId) {
7795
8213
  this.viewportId = viewportId;
7796
8214
  }
7797
- setViewportIndex(viewportIndex) {
7798
- this.viewportIndex = viewportIndex;
7799
- }
7800
8215
  setElement(element) {
7801
8216
  this.element = element;
7802
8217
  }
@@ -7806,9 +8221,6 @@ class ViewportInfo {
7806
8221
  getViewportData() {
7807
8222
  return this.viewportData;
7808
8223
  }
7809
- getViewportIndex() {
7810
- return this.viewportIndex;
7811
- }
7812
8224
  getElement() {
7813
8225
  return this.element;
7814
8226
  }
@@ -7819,13 +8231,14 @@ class ViewportInfo {
7819
8231
  // map the displaySetOptions and check if they are undefined then set them to default values
7820
8232
  const displaySetOptions = this.mapDisplaySetOptions(publicDisplaySetOptions);
7821
8233
  this.setDisplaySetOptions(displaySetOptions);
8234
+ return this.displaySetOptions;
7822
8235
  }
7823
8236
  hasDisplaySet(displaySetInstanceUID) {
7824
8237
  // Todo: currently this does not work for non image & referenceImage displaySets.
7825
8238
  // Since SEG and other derived displaySets are loaded in a different way, and not
7826
8239
  // via cornerstoneViewportService
7827
8240
  let viewportData = this.getViewportData();
7828
- if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC) {
8241
+ if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC || viewportData.viewportType === esm.Enums.ViewportType.VOLUME_3D) {
7829
8242
  viewportData = viewportData;
7830
8243
  return viewportData.data.some(_ref => {
7831
8244
  let {
@@ -7865,6 +8278,7 @@ class ViewportInfo {
7865
8278
  toolGroupId,
7866
8279
  presentationIds
7867
8280
  });
8281
+ return this.viewportOptions;
7868
8282
  }
7869
8283
  setViewportOptions(viewportOptions) {
7870
8284
  this.viewportOptions = viewportOptions;
@@ -7894,6 +8308,9 @@ class ViewportInfo {
7894
8308
  getOrientation() {
7895
8309
  return this.viewportOptions.orientation;
7896
8310
  }
8311
+ getDisplayArea() {
8312
+ return this.viewportOptions.displayArea;
8313
+ }
7897
8314
  getInitialImageOptions() {
7898
8315
  return this.viewportOptions.initialImageOptions;
7899
8316
  }
@@ -7959,7 +8376,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7959
8376
  constructor(servicesManager) {
7960
8377
  super(CornerstoneViewportService_EVENTS);
7961
8378
  this.renderingEngine = void 0;
7962
- this.viewportsInfo = new Map();
7963
8379
  this.viewportsById = new Map();
7964
8380
  this.viewportGridResizeObserver = void 0;
7965
8381
  this.viewportsDisplaySets = new Map();
@@ -7975,37 +8391,16 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7975
8391
 
7976
8392
  /**
7977
8393
  * Adds the HTML element to the viewportService
7978
- * @param {*} viewportIndex
8394
+ * @param {*} viewportId
7979
8395
  * @param {*} elementRef
7980
8396
  */
7981
- enableViewport(viewportIndex, viewportOptions, elementRef) {
7982
- // Use the provided viewportId
7983
- // Not providing a viewportId is frowned upon because it does weird things
7984
- // on moving them around, but it does mostly work.
7985
- if (!viewportOptions.viewportId) {
7986
- console.warn('Should provide viewport id externally', viewportOptions);
7987
- viewportOptions.viewportId = this.getViewportId(viewportIndex) || `viewport-${viewportIndex}`;
7988
- }
7989
- const {
7990
- viewportId
7991
- } = viewportOptions;
7992
- const viewportInfo = new Viewport(viewportIndex, viewportId);
7993
- if (!viewportInfo.viewportId) {
7994
- throw new Error('Should have viewport ID afterwards');
7995
- }
8397
+ enableViewport(viewportId, elementRef) {
8398
+ const viewportInfo = new Viewport(viewportId);
7996
8399
  viewportInfo.setElement(elementRef);
7997
- this.viewportsInfo.set(viewportIndex, viewportInfo);
7998
8400
  this.viewportsById.set(viewportId, viewportInfo);
7999
8401
  }
8000
8402
  getViewportIds() {
8001
- const viewportIds = [];
8002
- this.viewportsInfo.forEach(viewportInfo => {
8003
- viewportIds.push(viewportInfo.getViewportId());
8004
- });
8005
- return viewportIds;
8006
- }
8007
- getViewportId(viewportIndex) {
8008
- return this.viewportsInfo[viewportIndex]?.viewportId;
8403
+ return Array.from(this.viewportsById.keys());
8009
8404
  }
8010
8405
 
8011
8406
  /**
@@ -8059,34 +8454,38 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8059
8454
  * created for every new viewport, this will be called whenever the set of
8060
8455
  * viewports is changed, but NOT when the viewport position changes only.
8061
8456
  *
8062
- * @param viewportIndex
8457
+ * @param viewportId - The viewportId to disable
8063
8458
  */
8064
- disableElement(viewportIndex) {
8065
- const viewportInfo = this.viewportsInfo.get(viewportIndex);
8066
- if (!viewportInfo) {
8067
- return;
8068
- }
8069
- const viewportId = viewportInfo.getViewportId();
8070
- this.renderingEngine && this.renderingEngine.disableElement(viewportId);
8071
- this.viewportsInfo.get(viewportIndex).destroy();
8072
- this.viewportsInfo.delete(viewportIndex);
8459
+ disableElement(viewportId) {
8460
+ this.renderingEngine?.disableElement(viewportId);
8461
+
8462
+ // clean up
8073
8463
  this.viewportsById.delete(viewportId);
8464
+ this.viewportsDisplaySets.delete(viewportId);
8074
8465
  }
8075
8466
  setPresentations(viewport, presentations) {
8076
8467
  const properties = presentations?.lutPresentation?.properties;
8077
- if (properties) viewport.setProperties(properties);
8468
+ if (properties) {
8469
+ viewport.setProperties(properties);
8470
+ }
8078
8471
  const camera = presentations?.positionPresentation?.camera;
8079
- if (camera) viewport.setCamera(camera);
8472
+ if (camera) {
8473
+ viewport.setCamera(camera);
8474
+ }
8080
8475
  }
8081
- getPresentation(viewportIndex) {
8082
- const viewportInfo = this.viewportsInfo.get(viewportIndex);
8083
- if (!viewportInfo) return;
8476
+ getPresentation(viewportId) {
8477
+ const viewportInfo = this.viewportsById.get(viewportId);
8478
+ if (!viewportInfo) {
8479
+ return;
8480
+ }
8084
8481
  const {
8085
8482
  viewportType,
8086
8483
  presentationIds
8087
8484
  } = viewportInfo.getViewportOptions();
8088
- const csViewport = this.getCornerstoneViewportByIndex(viewportIndex);
8089
- if (!csViewport) return;
8485
+ const csViewport = this.getCornerstoneViewport(viewportId);
8486
+ if (!csViewport) {
8487
+ return;
8488
+ }
8090
8489
  const properties = csViewport.getProperties();
8091
8490
  if (properties.isComputedVOI) {
8092
8491
  delete properties.voiRange;
@@ -8102,59 +8501,111 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8102
8501
  camera
8103
8502
  };
8104
8503
  }
8504
+ storePresentation(_ref) {
8505
+ let {
8506
+ viewportId
8507
+ } = _ref;
8508
+ const stateSyncService = this.servicesManager.services.stateSyncService;
8509
+ let presentation;
8510
+ try {
8511
+ presentation = this.getPresentation(viewportId);
8512
+ } catch (error) {
8513
+ console.warn(error);
8514
+ }
8515
+ if (!presentation || !presentation.presentationIds) {
8516
+ return;
8517
+ }
8518
+ const {
8519
+ lutPresentationStore,
8520
+ positionPresentationStore
8521
+ } = stateSyncService.getState();
8522
+ const {
8523
+ presentationIds
8524
+ } = presentation;
8525
+ const {
8526
+ lutPresentationId,
8527
+ positionPresentationId
8528
+ } = presentationIds || {};
8529
+ const storeState = {};
8530
+ if (lutPresentationId) {
8531
+ storeState.lutPresentationStore = {
8532
+ ...lutPresentationStore,
8533
+ [lutPresentationId]: presentation
8534
+ };
8535
+ }
8536
+ if (positionPresentationId) {
8537
+ storeState.positionPresentationStore = {
8538
+ ...positionPresentationStore,
8539
+ [positionPresentationId]: presentation
8540
+ };
8541
+ }
8542
+ stateSyncService.store(storeState);
8543
+ }
8105
8544
 
8106
8545
  /**
8107
- * Uses the renderingEngine to enable the element for the given viewport index
8108
- * and sets the displaySet data to the viewport
8109
- * @param {*} viewportIndex
8110
- * @param {*} displaySet
8111
- * @param {*} dataSource
8112
- * @returns
8546
+ * Sets the viewport data for a viewport.
8547
+ * @param viewportId - The ID of the viewport to set the data for.
8548
+ * @param viewportData - The viewport data to set.
8549
+ * @param publicViewportOptions - The public viewport options.
8550
+ * @param publicDisplaySetOptions - The public display set options.
8551
+ * @param presentations - The presentations to set.
8113
8552
  */
8114
- setViewportData(viewportIndex, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8553
+ setViewportData(viewportId, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8115
8554
  const renderingEngine = this.getRenderingEngine();
8116
- const viewportId = publicViewportOptions.viewportId || this.getViewportId(viewportIndex);
8117
- if (!viewportId) {
8118
- throw new Error('Must define viewportId externally');
8119
- }
8555
+
8556
+ // This is the old viewportInfo, which may have old options but we might be
8557
+ // using its viewport (same viewportId as the new viewportInfo)
8120
8558
  const viewportInfo = this.viewportsById.get(viewportId);
8559
+
8560
+ // We should store the presentation for the current viewport since we can't only
8561
+ // rely to store it WHEN the viewport is disabled since we might keep around the
8562
+ // same viewport/element and just change the viewportData for it (drag and drop etc.)
8563
+ // the disableElement storePresentation handle would not be called in this case
8564
+ // and we would lose the presentation.
8565
+ this.storePresentation({
8566
+ viewportId: viewportInfo.getViewportId()
8567
+ });
8121
8568
  if (!viewportInfo) {
8122
- throw new Error('Viewport info not defined');
8569
+ throw new Error('element is not enabled for the given viewportId');
8123
8570
  }
8124
8571
 
8125
- // If the viewport has moved index, then record the new index
8126
- if (viewportInfo.viewportIndex !== viewportIndex) {
8127
- this.viewportsInfo.delete(viewportInfo.viewportIndex);
8128
- this.viewportsInfo.set(viewportIndex, viewportInfo);
8129
- viewportInfo.viewportIndex = viewportIndex;
8130
- }
8131
- viewportInfo.setRenderingEngineId(renderingEngine.id);
8132
- const {
8133
- viewportOptions,
8134
- displaySetOptions
8135
- } = this._getViewportAndDisplaySetOptions(publicViewportOptions, publicDisplaySetOptions, viewportInfo);
8136
- viewportInfo.setViewportOptions(viewportOptions);
8137
- viewportInfo.setDisplaySetOptions(displaySetOptions);
8138
- viewportInfo.setViewportData(viewportData);
8572
+ // override the viewportOptions and displaySetOptions with the public ones
8573
+ // since those are the newly set ones, we set them here so that it handles defaults
8574
+ const displaySetOptions = viewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
8575
+ const viewportOptions = viewportInfo.setPublicViewportOptions(publicViewportOptions);
8139
8576
  const element = viewportInfo.getElement();
8140
8577
  const type = viewportInfo.getViewportType();
8141
8578
  const background = viewportInfo.getBackground();
8142
8579
  const orientation = viewportInfo.getOrientation();
8580
+ const displayArea = viewportInfo.getDisplayArea();
8143
8581
  const viewportInput = {
8144
8582
  viewportId,
8145
8583
  element,
8146
8584
  type,
8147
8585
  defaultOptions: {
8148
8586
  background,
8149
- orientation
8587
+ orientation,
8588
+ displayArea
8150
8589
  }
8151
8590
  };
8152
8591
 
8592
+ // Rendering Engine Id set should happen before enabling the element
8593
+ // since there are callbacks that depend on the renderingEngine id
8594
+ // Todo: however, this is a limitation which means that we can't change
8595
+ // the rendering engine id for a given viewport which might be a super edge
8596
+ // case
8597
+ viewportInfo.setRenderingEngineId(renderingEngine.id);
8598
+
8153
8599
  // Todo: this is not optimal at all, we are re-enabling the already enabled
8154
8600
  // element which is not what we want. But enabledElement as part of the
8155
8601
  // renderingEngine is designed to be used like this. This will trigger
8156
8602
  // ENABLED_ELEMENT again and again, which will run onEnableElement callbacks
8157
8603
  renderingEngine.enableElement(viewportInput);
8604
+ viewportInfo.setViewportOptions(viewportOptions);
8605
+ viewportInfo.setDisplaySetOptions(displaySetOptions);
8606
+ viewportInfo.setViewportData(viewportData);
8607
+ viewportInfo.setViewportId(viewportId);
8608
+ this.viewportsById.set(viewportId, viewportInfo);
8158
8609
  const viewport = renderingEngine.getViewport(viewportId);
8159
8610
  this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
8160
8611
 
@@ -8163,7 +8614,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8163
8614
  // invalid data.
8164
8615
  this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
8165
8616
  viewportData,
8166
- viewportIndex,
8167
8617
  viewportId
8168
8618
  });
8169
8619
  }
@@ -8175,31 +8625,8 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8175
8625
  const viewport = this.renderingEngine.getViewport(viewportId);
8176
8626
  return viewport;
8177
8627
  }
8178
- getCornerstoneViewportByIndex(viewportIndex) {
8179
- const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
8180
- if (!viewportInfo || !this.renderingEngine || this.renderingEngine.hasBeenDestroyed) {
8181
- return null;
8182
- }
8183
- const viewport = this.renderingEngine.getViewport(viewportInfo.getViewportId());
8184
- return viewport;
8185
- }
8186
-
8187
- /**
8188
- * Returns the viewportIndex for the provided viewportId
8189
- * @param {string} viewportId - the viewportId
8190
- * @returns {number} - the viewportIndex
8191
- */
8192
- getViewportInfoByIndex(viewportIndex) {
8193
- return this.viewportsInfo.get(viewportIndex);
8194
- }
8195
8628
  getViewportInfo(viewportId) {
8196
- // @ts-ignore
8197
- for (const [index, viewport] of this.viewportsInfo.entries()) {
8198
- if (viewport.getViewportId() === viewportId) {
8199
- return viewport;
8200
- }
8201
- }
8202
- return null;
8629
+ return this.viewportsById.get(viewportId);
8203
8630
  }
8204
8631
  _setStackViewport(viewport, viewportData, viewportInfo, presentations) {
8205
8632
  const displaySetOptions = viewportInfo.getDisplaySetOptions();
@@ -8236,9 +8663,13 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8236
8663
  }
8237
8664
  }
8238
8665
  viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
8239
- viewport.setProperties(properties);
8666
+ viewport.setProperties({
8667
+ ...properties
8668
+ });
8240
8669
  const camera = presentations.positionPresentation?.camera;
8241
- if (camera) viewport.setCamera(camera);
8670
+ if (camera) {
8671
+ viewport.setCamera(camera);
8672
+ }
8242
8673
  });
8243
8674
  }
8244
8675
  _getInitialImageIndexForViewport(viewportInfo, imageIds) {
@@ -8333,7 +8764,7 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8333
8764
  });
8334
8765
  }
8335
8766
  this.viewportsDisplaySets.set(viewport.id, displaySetInstanceUIDs);
8336
- if (hangingProtocolService.hasCustomImageLoadStrategy() && !hangingProtocolService.customImageLoadPerformed) {
8767
+ if (hangingProtocolService.getShouldPerformCustomImageLoad()) {
8337
8768
  // delegate the volume loading to the hanging protocol service if it has a custom image load strategy
8338
8769
  return hangingProtocolService.runImageLoadStrategy({
8339
8770
  viewportId: viewport.id,
@@ -8395,11 +8826,11 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8395
8826
  };
8396
8827
  });
8397
8828
  await viewport.setVolumes(volumeInputArray);
8398
- volumesProperties.forEach(_ref => {
8829
+ volumesProperties.forEach(_ref2 => {
8399
8830
  let {
8400
8831
  properties,
8401
8832
  volumeId
8402
- } = _ref;
8833
+ } = _ref2;
8403
8834
  viewport.setProperties(properties, volumeId);
8404
8835
  });
8405
8836
  this.setPresentations(viewport, presentations);
@@ -8448,13 +8879,25 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8448
8879
  continue;
8449
8880
  }
8450
8881
 
8451
- // otherwise, check if the hydrated segmentations are in the same FOR
8882
+ // otherwise, check if the hydrated segmentations are in the same FrameOfReferenceUID
8452
8883
  // as the primary displaySet, if so add the representation (since it was not there)
8453
8884
  const {
8454
- id: segDisplaySetInstanceUID,
8455
- type
8885
+ id: segDisplaySetInstanceUID
8456
8886
  } = segmentation;
8457
- const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8887
+ let segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8888
+ if (!segFrameOfReferenceUID) {
8889
+ // if the segmentation displaySet does not have a FrameOfReferenceUID, we might check the
8890
+ // segmentation itself maybe it has a FrameOfReferenceUID
8891
+ const {
8892
+ FrameOfReferenceUID
8893
+ } = segmentation;
8894
+ if (FrameOfReferenceUID) {
8895
+ segFrameOfReferenceUID = FrameOfReferenceUID;
8896
+ }
8897
+ }
8898
+ if (!segFrameOfReferenceUID) {
8899
+ return;
8900
+ }
8458
8901
  let shouldDisplaySeg = false;
8459
8902
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
8460
8903
  const primaryFrameOfReferenceUID = this._getFrameOfReferenceUID(displaySetInstanceUID);
@@ -8487,13 +8930,12 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8487
8930
 
8488
8931
  // Todo: keepCamera is an interim solution until we have a better solution for
8489
8932
  // keeping the camera position when the viewport data is changed
8490
- updateViewport(viewportIndex, viewportData) {
8933
+ updateViewport(viewportId, viewportData) {
8491
8934
  let keepCamera = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8492
- const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
8493
- const viewportId = viewportInfo.getViewportId();
8935
+ const viewportInfo = this.getViewportInfo(viewportId);
8494
8936
  const viewport = this.getCornerstoneViewport(viewportId);
8495
8937
  const viewportCamera = viewport.getCamera();
8496
- if (viewport instanceof esm.VolumeViewport) {
8938
+ if (viewport instanceof esm.VolumeViewport || viewport instanceof esm.VolumeViewport3D) {
8497
8939
  this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
8498
8940
  if (keepCamera) {
8499
8941
  viewport.setCamera(viewportCamera);
@@ -8548,23 +8990,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8548
8990
  return slabThickness;
8549
8991
  }
8550
8992
  }
8551
- _getViewportAndDisplaySetOptions(publicViewportOptions, publicDisplaySetOptions, viewportInfo) {
8552
- const viewportIndex = viewportInfo.getViewportIndex();
8553
-
8554
- // Creating a temporary viewportInfo to handle defaults
8555
- const newViewportInfo = new Viewport(viewportIndex, viewportInfo.getViewportId());
8556
-
8557
- // To handle setting the default values if missing for the viewportOptions and
8558
- // displaySetOptions
8559
- newViewportInfo.setPublicViewportOptions(publicViewportOptions);
8560
- newViewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
8561
- const newViewportOptions = newViewportInfo.getViewportOptions();
8562
- const newDisplaySetOptions = newViewportInfo.getDisplaySetOptions();
8563
- return {
8564
- viewportOptions: newViewportOptions,
8565
- displaySetOptions: newDisplaySetOptions
8566
- };
8567
- }
8568
8993
  _getFrameOfReferenceUID(displaySetInstanceUID) {
8569
8994
  const {
8570
8995
  displaySetService
@@ -8602,28 +9027,28 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8602
9027
  *
8603
9028
  * @param measurement
8604
9029
  * The measurement that is desired to view.
8605
- * @param activeViewportIndex - the index that was active at the time the jump
9030
+ * @param activeViewportId - the index that was active at the time the jump
8606
9031
  * was initiated.
8607
- * @return the viewportIndex to display the given measurement
9032
+ * @return the viewportId that the measurement should be displayed in.
8608
9033
  */
8609
- getViewportIndexToJump(activeViewportIndex, displaySetInstanceUID, cameraProps) {
8610
- const viewportInfo = this.viewportsInfo.get(activeViewportIndex);
9034
+ getViewportIdToJump(activeViewportId, displaySetInstanceUID, cameraProps) {
9035
+ const viewportInfo = this.getViewportInfo(activeViewportId);
8611
9036
  const {
8612
9037
  referencedImageId
8613
9038
  } = cameraProps;
8614
9039
  if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
8615
- return activeViewportIndex;
9040
+ return activeViewportId;
8616
9041
  }
8617
- return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportIndex ?? -1;
9042
+ return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportId ?? null;
8618
9043
  }
8619
9044
  }
8620
9045
  CornerstoneViewportService.REGISTRATION = {
8621
9046
  name: 'cornerstoneViewportService',
8622
9047
  altName: 'CornerstoneViewportService',
8623
- create: _ref2 => {
9048
+ create: _ref3 => {
8624
9049
  let {
8625
9050
  servicesManager
8626
- } = _ref2;
9051
+ } = _ref3;
8627
9052
  return new CornerstoneViewportService(servicesManager);
8628
9053
  }
8629
9054
  };
@@ -8632,7 +9057,7 @@ CornerstoneViewportService.REGISTRATION = {
8632
9057
 
8633
9058
 
8634
9059
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
8635
- var dicomweb_client_es = __webpack_require__(75935);
9060
+ var dicomweb_client_es = __webpack_require__(97604);
8636
9061
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/dicomLoaderService.js
8637
9062
 
8638
9063
 
@@ -8707,7 +9132,7 @@ class DicomLoaderService {
8707
9132
  // Use referenced imageInstance
8708
9133
  const imageInstance = getImageInstance(dataset);
8709
9134
  const nonImageInstance = getNonImageInstance(dataset);
8710
- if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId.startsWith('dicomfile')) {
9135
+ if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId?.startsWith('dicomfile')) {
8711
9136
  return;
8712
9137
  }
8713
9138
  const instance = imageInstance || nonImageInstance;
@@ -8801,30 +9226,6 @@ const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-cornerstone"}
8801
9226
 
8802
9227
  const id = package_namespaceObject.u2;
8803
9228
 
8804
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js
8805
- function getHandlesFromPoints(points) {
8806
- if (points.longAxis && points.shortAxis) {
8807
- const handles = {};
8808
- handles.start = points.longAxis[0];
8809
- handles.end = points.longAxis[1];
8810
- handles.perpendicularStart = points.longAxis[0];
8811
- handles.perpendicularEnd = points.longAxis[1];
8812
- return handles;
8813
- }
8814
- return points.map((p, i) => i % 10 === 0 ? {
8815
- start: p
8816
- } : {
8817
- end: p
8818
- }).reduce((obj, item) => Object.assign(obj, {
8819
- ...item
8820
- }), {});
8821
- }
8822
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/index.ts
8823
-
8824
-
8825
-
8826
-
8827
-
8828
9229
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/index.ts
8829
9230
 
8830
9231
 
@@ -8849,11 +9250,10 @@ function src_extends() { src_extends = Object.assign ? Object.assign.bind() : fu
8849
9250
 
8850
9251
 
8851
9252
 
8852
-
8853
9253
 
8854
9254
 
8855
9255
  const Component = /*#__PURE__*/react.lazy(() => {
8856
- return Promise.all(/* import() */[__webpack_require__.e(664), __webpack_require__.e(351)]).then(__webpack_require__.bind(__webpack_require__, 30351));
9256
+ return Promise.all(/* import() */[__webpack_require__.e(23), __webpack_require__.e(181)]).then(__webpack_require__.bind(__webpack_require__, 86181));
8857
9257
  });
8858
9258
  const OHIFCornerstoneViewport = props => {
8859
9259
  return /*#__PURE__*/react.createElement(react.Suspense, {
@@ -8876,7 +9276,6 @@ const cornerstoneExtension = {
8876
9276
  esm.imageLoadPoolManager.clearRequestStack(type);
8877
9277
  esm.imageRetrievalPoolManager.clearRequestStack(type);
8878
9278
  });
8879
- destroy();
8880
9279
  (0,state/* reset */.mc)();
8881
9280
  },
8882
9281
  /**
@@ -8956,7 +9355,7 @@ const cornerstoneExtension = {
8956
9355
 
8957
9356
  /***/ }),
8958
9357
 
8959
- /***/ 21922:
9358
+ /***/ 73704:
8960
9359
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8961
9360
 
8962
9361
  "use strict";
@@ -8976,21 +9375,21 @@ const state = {
8976
9375
  * @param {HTMLElement} dom Active viewport element.
8977
9376
  * @return void
8978
9377
  */
8979
- const setEnabledElement = (viewportIndex, element, context) => {
9378
+ const setEnabledElement = (viewportId, element, context) => {
8980
9379
  const targetContext = context || state.DEFAULT_CONTEXT;
8981
- state.enabledElements[viewportIndex] = {
9380
+ state.enabledElements[viewportId] = {
8982
9381
  element,
8983
9382
  context: targetContext
8984
9383
  };
8985
9384
  };
8986
9385
 
8987
9386
  /**
8988
- * Grabs the enabled element `dom` reference of an ative viewport.
9387
+ * Grabs the enabled element `dom` reference of an active viewport.
8989
9388
  *
8990
9389
  * @return {HTMLElement} Active viewport element.
8991
9390
  */
8992
- const getEnabledElement = viewportIndex => {
8993
- return state.enabledElements[viewportIndex];
9391
+ const getEnabledElement = viewportId => {
9392
+ return state.enabledElements[viewportId];
8994
9393
  };
8995
9394
  const reset = () => {
8996
9395
  state.enabledElements = {};
@@ -8999,14 +9398,14 @@ const reset = () => {
8999
9398
 
9000
9399
  /***/ }),
9001
9400
 
9002
- /***/ 63130:
9401
+ /***/ 87172:
9003
9402
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9004
9403
 
9005
9404
  "use strict";
9006
9405
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9007
9406
  /* harmony export */ Z: () => (/* binding */ getSOPInstanceAttributes)
9008
9407
  /* harmony export */ });
9009
- /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77331);
9408
+ /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3743);
9010
9409
 
9011
9410
 
9012
9411
  /**