@ohif/app 3.7.0-beta.8 → 3.7.0-beta.81

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.d238efac58b7fe8fd1a3.js → 12.bundle.f5ca9901f923a487d8b3.js} +6 -6
  2. package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.253395f320b72180da63.js} +6 -6
  3. package/dist/{208.bundle.37c2a73b3387e657568c.js → 128.bundle.2dfd63dcf3b846809dfb.js} +41 -59
  4. package/dist/{351.bundle.0742237651aef9694a65.js → 181.bundle.fa57199595cf28f44c7b.js} +226 -204
  5. package/dist/{351.css → 181.css} +1 -1
  6. package/dist/{744.bundle.23011553706b687f67e4.js → 19.bundle.0d74cbc1db3841b851a6.js} +240 -375
  7. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.96bbb4547a346fe3921f.js} +1420 -750
  8. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  9. package/dist/221.bundle.933ef6dcf6d29eea0259.js +1722 -0
  10. package/dist/221.css +2 -0
  11. package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
  12. package/dist/{976.bundle.dcbddf56a7d8f388bb8a.js → 236.bundle.3f6a0e3dd259bc60d4d0.js} +87 -103
  13. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.36909d7bc681d66087d1.js} +52 -36
  14. package/dist/{973.bundle.03b016e6095622adf12f.js → 281.bundle.69d85dc35d1bc1335fbd.js} +18 -14
  15. package/dist/{82.bundle.10c2133333748ec6fba0.js → 342.bundle.46b3527fb38b74a044e8.js} +1768 -475
  16. package/dist/{192.bundle.2dc14a6e3c4c6be913b0.js → 348.bundle.46d4c41b9ae681c50903.js} +86 -73
  17. package/dist/{404.bundle.5d57295bc05206092d42.js → 359.bundle.408fa86b95fd1597552c.js} +46 -131
  18. package/dist/{790.bundle.cedf27deeed29266a92b.js → 410.bundle.bf702dcec9db605d0d72.js} +11 -9
  19. package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.af0a207c29b109f84159.js} +49 -17
  20. package/dist/{569.bundle.c8e771a8d28e237b32be.js → 451.bundle.dc0d14c3724464cca2bf.js} +86 -106
  21. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.2206d4c0ac2ad0df2362.js} +78 -99
  22. package/dist/{199.bundle.e4ac6606dd62e42e9da4.js → 506.bundle.90364a62dcd8e433c1a4.js} +11 -9
  23. package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 530.bundle.a03b6f942ace3e1baa1e.js} +726 -447
  24. package/dist/579.css +1 -0
  25. package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
  26. package/dist/613.bundle.a06431b9a1b14cb8f469.js +532 -0
  27. package/dist/{984.bundle.e7dcbd3b8992748823fb.js → 663.bundle.482bdfb562bfb3700c55.js} +68 -38
  28. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
  29. package/dist/{50.bundle.424f8d05f1bebaafcf2c.js → 687.bundle.b456dd5eae1afeaab05d.js} +218 -9
  30. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 754.bundle.e3ce6855d8d4d187f224.js} +12423 -7549
  31. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.d365320749c4f67cda70.js} +93 -64
  32. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  33. package/dist/{270.bundle.4564621556b0f963a004.js → 777.bundle.ae3fdb8a470caecc0c6a.js} +1330 -929
  34. package/dist/{283.bundle.085cddb2f16e430677b0.js → 782.bundle.742caefc99a1393225d2.js} +117 -67
  35. package/dist/{642.bundle.25e56df5f0bcd2c729b4.js → 814.bundle.f84f8c34cd82d9456367.js} +6 -6
  36. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.5fca45a4f40f5259aec5.js} +81 -34
  37. package/dist/831.bundle.83658f62fcc769043605.js +16700 -0
  38. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.a9de117c3a42b7907cf6.js} +34 -29
  39. package/dist/945.min.worker.js +1 -1
  40. package/dist/945.min.worker.js.map +1 -1
  41. package/dist/{707.bundle.17a9d7352409b6269596.js → 967.bundle.aa7e40475fa7cab0726e.js} +895 -572
  42. package/dist/_redirects +1 -1
  43. package/dist/app-config.js +35 -17
  44. package/dist/{app.bundle.837996a8f7dd2aaac338.js → app.bundle.91713b6c13b1264236f9.js} +73339 -68140
  45. package/dist/app.bundle.css +13 -12
  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 +1 -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 +20 -21
  61. package/dist/616.bundle.eb4887184da6c57bf7a3.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 → 967.css} +0 -0
@@ -1,6 +1,6 @@
1
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[707],{
1
+ (self["webpackChunk"] = self["webpackChunk"] || []).push([[967],{
2
2
 
3
- /***/ 31197:
3
+ /***/ 94004:
4
4
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5
5
 
6
6
  "use strict";
@@ -9,7 +9,7 @@ __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),
@@ -26,27 +26,26 @@ __webpack_require__.r(utils_namespaceObject);
26
26
  __webpack_require__.d(utils_namespaceObject, {
27
27
  getFirstAnnotationSelected: () => (getFirstAnnotationSelected),
28
28
  getHandlesFromPoints: () => (getHandlesFromPoints),
29
- getModalityUnit: () => (utils_getModalityUnit),
30
29
  getSOPInstanceAttributes: () => (getSOPInstanceAttributes/* default */.Z),
31
30
  isAnnotationSelected: () => (isAnnotationSelected),
32
31
  setAnnotationSelected: () => (setAnnotationSelected)
33
32
  });
34
33
 
35
34
  // 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);
35
+ var react = __webpack_require__(43001);
36
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 337 modules
37
+ var esm = __webpack_require__(45754);
38
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 327 modules
39
+ var dist_esm = __webpack_require__(99777);
40
+ // EXTERNAL MODULE: ../../core/src/index.ts + 75 modules
41
+ var src = __webpack_require__(34491);
43
42
  // 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);
43
+ var streaming_image_volume_loader_dist_esm = __webpack_require__(7087);
45
44
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/dicom-image-loader/dist/dynamic-import/cornerstoneDICOMImageLoader.min.js
46
- var cornerstoneDICOMImageLoader_min = __webpack_require__(99605);
45
+ var cornerstoneDICOMImageLoader_min = __webpack_require__(61539);
47
46
  var cornerstoneDICOMImageLoader_min_default = /*#__PURE__*/__webpack_require__.n(cornerstoneDICOMImageLoader_min);
48
47
  // EXTERNAL MODULE: ../../../node_modules/dicom-parser/dist/dicomParser.min.js
49
- var dicomParser_min = __webpack_require__(43264);
48
+ var dicomParser_min = __webpack_require__(56660);
50
49
  var dicomParser_min_default = /*#__PURE__*/__webpack_require__.n(dicomParser_min);
51
50
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/initWADOImageLoader.js
52
51
 
@@ -76,7 +75,7 @@ function initWebWorkers(appConfig) {
76
75
  initialized = true;
77
76
  }
78
77
  }
79
- function initWADOImageLoader(userAuthenticationService, appConfig) {
78
+ function initWADOImageLoader(userAuthenticationService, appConfig, extensionManager) {
80
79
  (cornerstoneDICOMImageLoader_min_default()).external.cornerstone = esm;
81
80
  (cornerstoneDICOMImageLoader_min_default()).external.dicomParser = (dicomParser_min_default());
82
81
  registerVolumeLoader('cornerstoneStreamingImageVolume', streaming_image_volume_loader_dist_esm/* cornerstoneStreamingImageVolumeLoader */.IU);
@@ -87,21 +86,17 @@ function initWADOImageLoader(userAuthenticationService, appConfig) {
87
86
  // will convert everything to integers (to be able to work with cornerstone-2d).
88
87
  // Until the default is set to true (which is the case for cornerstone3D),
89
88
  // we should set this flag to false.
90
- convertFloatPixelDataToInt: false
89
+ convertFloatPixelDataToInt: false,
90
+ use16BitDataType: Boolean(appConfig.use16BitDataType)
91
91
  },
92
92
  beforeSend: function (xhr) {
93
+ //TODO should be removed in the future and request emitted by DicomWebDataSource
94
+ const sourceConfig = extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
93
95
  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
96
+ const acceptHeader = src.utils.generateAcceptHeader(sourceConfig.acceptHeader, sourceConfig.requestTransferSyntaxUID, sourceConfig.omitQuotationForMultipartRequest);
100
97
  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=*',
98
+ Accept: acceptHeader
103
99
  };
104
-
105
100
  if (headers) {
106
101
  Object.assign(xhrRequestHeaders, headers);
107
102
  }
@@ -116,14 +111,14 @@ function initWADOImageLoader(userAuthenticationService, appConfig) {
116
111
  function destroy() {
117
112
  // Note: we don't want to call .terminate on the webWorkerManager since
118
113
  // that resets the config
119
- const webWorkers = cornerstoneDICOMImageLoader_min.webWorkerManager.webWorkers;
114
+ const webWorkers = webWorkerManager.webWorkers;
120
115
  for (let i = 0; i < webWorkers.length; i++) {
121
116
  webWorkers[i].worker.terminate();
122
117
  }
123
118
  webWorkers.length = 0;
124
119
  }
125
- // EXTERNAL MODULE: ../../ui/src/index.js + 452 modules
126
- var ui_src = __webpack_require__(28619);
120
+ // EXTERNAL MODULE: ../../ui/src/index.js + 485 modules
121
+ var ui_src = __webpack_require__(71783);
127
122
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/callInputDialog.tsx
128
123
 
129
124
 
@@ -157,7 +152,9 @@ function callInputDialog(uiDialogService, data, callback) {
157
152
  } = _ref;
158
153
  switch (action.id) {
159
154
  case 'save':
160
- if (typeof validateFunc === 'function' && !validateFunc(value.label)) return;
155
+ if (typeof validateFunc === 'function' && !validateFunc(value.label)) {
156
+ return;
157
+ }
161
158
  callback(value.label, action.id);
162
159
  break;
163
160
  case 'cancel':
@@ -187,11 +184,11 @@ function callInputDialog(uiDialogService, data, callback) {
187
184
  actions: [{
188
185
  id: 'cancel',
189
186
  text: 'Cancel',
190
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary
187
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary
191
188
  }, {
192
189
  id: 'save',
193
190
  text: 'Save',
194
- type: ui_src/* ButtonEnums.type */.LZ.U.primary
191
+ type: ui_src/* ButtonEnums.type */.LZ.dt.primary
195
192
  }],
196
193
  onSubmit: onSubmitHandler,
197
194
  body: _ref2 => {
@@ -201,7 +198,7 @@ function callInputDialog(uiDialogService, data, callback) {
201
198
  } = _ref2;
202
199
  return /*#__PURE__*/react.createElement(ui_src/* Input */.II, {
203
200
  autoFocus: true,
204
- className: "bg-black border-primary-main",
201
+ className: "border-primary-main bg-black",
205
202
  type: "text",
206
203
  id: "annotation",
207
204
  label: inputLabel,
@@ -232,17 +229,17 @@ function callInputDialog(uiDialogService, data, callback) {
232
229
  }
233
230
  /* harmony default export */ const utils_callInputDialog = (callInputDialog);
234
231
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/state.ts
235
- var state = __webpack_require__(21922);
232
+ var state = __webpack_require__(73704);
236
233
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/getActiveViewportEnabledElement.ts
237
234
 
238
235
 
239
236
  function getActiveViewportEnabledElement(viewportGridService) {
240
237
  const {
241
- activeViewportIndex
238
+ activeViewportId
242
239
  } = viewportGridService.getState();
243
240
  const {
244
241
  element
245
- } = (0,state/* getEnabledElement */.K8)(activeViewportIndex) || {};
242
+ } = (0,state/* getEnabledElement */.K8)(activeViewportId) || {};
246
243
  const enabledElement = (0,esm.getEnabledElement)(element);
247
244
  return enabledElement;
248
245
  }
@@ -322,11 +319,12 @@ function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
322
319
  const currentColumnPixelSpacing = calibratedPixelSpacing?.[1] || imagePlaneModule?.columnPixelSpacing || 1;
323
320
  const adjustCalibration = newLength => {
324
321
  const spacingScale = newLength / length;
325
- const rowSpacing = spacingScale * currentRowPixelSpacing;
326
- const colSpacing = spacingScale * currentColumnPixelSpacing;
327
322
 
328
323
  // trigger resize of the viewport to adjust the world/pixel mapping
329
- calibrateImageSpacing(imageId, viewport.getRenderingEngine(), rowSpacing, colSpacing);
324
+ calibrateImageSpacing(imageId, viewport.getRenderingEngine(), {
325
+ type: 'User',
326
+ scale: 1 / spacingScale
327
+ });
330
328
  };
331
329
  return new Promise((resolve, reject) => {
332
330
  if (!uiDialogService) {
@@ -358,9 +356,221 @@ function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
358
356
  });
359
357
  });
360
358
  }
359
+ // EXTERNAL MODULE: ../../core/src/utils/index.js + 25 modules
360
+ var utils = __webpack_require__(77250);
361
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx
362
+
363
+
364
+
365
+
366
+ /**
367
+ * Image Overlay Viewer tool is not a traditional tool that requires user interactin.
368
+ * But it is used to display Pixel Overlays. And it will provide toggling capability.
369
+ *
370
+ * The documentation for Overlay Plane Module of DICOM can be found in [C.9.2 of
371
+ * Part-3 of DICOM standard](https://dicom.nema.org/medical/dicom/2018b/output/chtml/part03/sect_C.9.2.html)
372
+ *
373
+ * Image Overlay rendered by this tool can be toggled on and off using
374
+ * toolGroup.setToolEnabled() and toolGroup.setToolDisabled()
375
+ */
376
+ class ImageOverlayViewerTool extends dist_esm.AnnotationDisplayTool {
377
+ constructor() {
378
+ let toolProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
379
+ let defaultToolProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
380
+ supportedInteractionTypes: [],
381
+ configuration: {
382
+ fillColor: [255, 127, 127, 255]
383
+ }
384
+ };
385
+ super(toolProps, defaultToolProps);
386
+ this._cachedOverlayMetadata = new Map();
387
+ this._cachedStats = {};
388
+ this.onSetToolDisabled = () => {
389
+ this._cachedStats = {};
390
+ this._cachedOverlayMetadata = new Map();
391
+ };
392
+ this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
393
+ const {
394
+ viewport
395
+ } = enabledElement;
396
+ const imageId = this.getReferencedImageId(viewport);
397
+ if (!imageId) {
398
+ return;
399
+ }
400
+ const overlays = this._cachedOverlayMetadata.get(imageId) ?? esm.metaData.get('overlayPlaneModule', imageId)?.overlays;
401
+
402
+ // no overlays
403
+ if (!overlays?.length) {
404
+ return;
405
+ }
406
+ this._cachedOverlayMetadata.set(imageId, overlays);
407
+ this._getCachedStat(imageId, overlays, this.configuration.fillColor).then(cachedStat => {
408
+ cachedStat.overlays.forEach(overlay => {
409
+ this._renderOverlay(enabledElement, svgDrawingHelper, overlay);
410
+ });
411
+ });
412
+ return true;
413
+ };
414
+ }
415
+ getReferencedImageId(viewport) {
416
+ if (viewport instanceof esm.VolumeViewport) {
417
+ return;
418
+ }
419
+ const targetId = this.getTargetId(viewport);
420
+ return targetId.split('imageId:')[1];
421
+ }
422
+ /**
423
+ * Render to DOM
424
+ *
425
+ * @param enabledElement
426
+ * @param svgDrawingHelper
427
+ * @param overlayData
428
+ * @returns
429
+ */
430
+ _renderOverlay(enabledElement, svgDrawingHelper, overlayData) {
431
+ const {
432
+ viewport
433
+ } = enabledElement;
434
+ const imageId = this.getReferencedImageId(viewport);
435
+ if (!imageId) {
436
+ return;
437
+ }
438
+
439
+ // Decide the rendering position of the overlay image on the current canvas
440
+ const {
441
+ _id,
442
+ columns: width,
443
+ rows: height,
444
+ x,
445
+ y
446
+ } = overlayData;
447
+ const overlayTopLeftWorldPos = esm.utilities.imageToWorldCoords(imageId, [x - 1,
448
+ // Remind that top-left corner's (x, y) is be (1, 1)
449
+ y - 1]);
450
+ const overlayTopLeftOnCanvas = viewport.worldToCanvas(overlayTopLeftWorldPos);
451
+ const overlayBottomRightWorldPos = esm.utilities.imageToWorldCoords(imageId, [width, height]);
452
+ const overlayBottomRightOnCanvas = viewport.worldToCanvas(overlayBottomRightWorldPos);
453
+
454
+ // add image to the annotations svg layer
455
+ const svgns = 'http://www.w3.org/2000/svg';
456
+ const svgNodeHash = `image-overlay-${_id}`;
457
+ const existingImageElement = svgDrawingHelper.getSvgNode(svgNodeHash);
458
+ const attributes = {
459
+ 'data-id': svgNodeHash,
460
+ width: overlayBottomRightOnCanvas[0] - overlayTopLeftOnCanvas[0],
461
+ height: overlayBottomRightOnCanvas[1] - overlayTopLeftOnCanvas[1],
462
+ x: overlayTopLeftOnCanvas[0],
463
+ y: overlayTopLeftOnCanvas[1],
464
+ href: overlayData.dataUrl
465
+ };
466
+ if (isNaN(attributes.x) || isNaN(attributes.y) || isNaN(attributes.width) || isNaN(attributes.height)) {
467
+ console.warn('Invalid rendering attribute for image overlay', attributes['data-id']);
468
+ return false;
469
+ }
470
+ if (existingImageElement) {
471
+ dist_esm.drawing.setAttributesIfNecessary(attributes, existingImageElement);
472
+ svgDrawingHelper.setNodeTouched(svgNodeHash);
473
+ } else {
474
+ const newImageElement = document.createElementNS(svgns, 'image');
475
+ dist_esm.drawing.setNewAttributesIfValid(attributes, newImageElement);
476
+ svgDrawingHelper.appendNode(newImageElement, svgNodeHash);
477
+ }
478
+ return true;
479
+ }
480
+ async _getCachedStat(imageId, overlayMetadata, color) {
481
+ if (this._cachedStats[imageId] && this._isSameColor(this._cachedStats[imageId].color, color)) {
482
+ return this._cachedStats[imageId];
483
+ }
484
+ const overlays = await Promise.all(overlayMetadata.filter(overlay => overlay.pixelData).map(async (overlay, idx) => {
485
+ let pixelData = null;
486
+ if (overlay.pixelData.Value) {
487
+ pixelData = overlay.pixelData.Value;
488
+ } else if (overlay.pixelData.retrieveBulkData) {
489
+ pixelData = await overlay.pixelData.retrieveBulkData();
490
+ }
491
+ if (!pixelData) {
492
+ return;
493
+ }
494
+ const dataUrl = this._renderOverlayToDataUrl({
495
+ width: overlay.columns,
496
+ height: overlay.rows
497
+ }, color, pixelData);
498
+ return {
499
+ ...overlay,
500
+ _id: (0,utils/* guid */.M8)(),
501
+ dataUrl,
502
+ // this will be a data url expression of the rendered image
503
+ color
504
+ };
505
+ }));
506
+ this._cachedStats[imageId] = {
507
+ color: color,
508
+ overlays: overlays.filter(overlay => overlay)
509
+ };
510
+ return this._cachedStats[imageId];
511
+ }
512
+
513
+ /**
514
+ * compare two RGBA expression of colors.
515
+ *
516
+ * @param color1
517
+ * @param color2
518
+ * @returns
519
+ */
520
+ _isSameColor(color1, color2) {
521
+ return color1 && color2 && color1[0] === color2[0] && color1[1] === color2[1] && color1[2] === color2[2] && color1[3] === color2[3];
522
+ }
523
+
524
+ /**
525
+ * pixelData of overlayPlane module is an array of bits corresponding
526
+ * to each of the underlying pixels of the image.
527
+ * Let's create pixel data from bit array of overlay data
528
+ *
529
+ * @param pixelDataRaw
530
+ * @param color
531
+ * @returns
532
+ */
533
+ _renderOverlayToDataUrl(_ref, color, pixelDataRaw) {
534
+ let {
535
+ width,
536
+ height
537
+ } = _ref;
538
+ const pixelDataView = new DataView(pixelDataRaw);
539
+ const totalBits = width * height;
540
+ const canvas = document.createElement('canvas');
541
+ canvas.width = width;
542
+ canvas.height = height;
543
+ const ctx = canvas.getContext('2d');
544
+ ctx.clearRect(0, 0, width, height); // make it transparent
545
+ ctx.globalCompositeOperation = 'copy';
546
+ const imageData = ctx.getImageData(0, 0, width, height);
547
+ const data = imageData.data;
548
+ for (let i = 0, bitIdx = 0, byteIdx = 0; i < totalBits; i++) {
549
+ if (pixelDataView.getUint8(byteIdx) & 1 << bitIdx) {
550
+ data[i * 4] = color[0];
551
+ data[i * 4 + 1] = color[1];
552
+ data[i * 4 + 2] = color[2];
553
+ data[i * 4 + 3] = color[3];
554
+ }
555
+
556
+ // next bit, byte
557
+ if (bitIdx >= 7) {
558
+ bitIdx = 0;
559
+ byteIdx++;
560
+ } else {
561
+ bitIdx++;
562
+ }
563
+ }
564
+ ctx.putImageData(imageData, 0, 0);
565
+ return canvas.toDataURL();
566
+ }
567
+ }
568
+ ImageOverlayViewerTool.toolName = 'ImageOverlayViewer';
569
+ /* harmony default export */ const tools_ImageOverlayViewerTool = (ImageOverlayViewerTool);
361
570
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/initCornerstoneTools.js
362
571
 
363
572
 
573
+
364
574
  function initCornerstoneTools() {
365
575
  let configuration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
366
576
  dist_esm.CrosshairsTool.isAnnotation = false;
@@ -390,6 +600,10 @@ function initCornerstoneTools() {
390
600
  (0,dist_esm.addTool)(dist_esm.ReferenceLinesTool);
391
601
  (0,dist_esm.addTool)(tools_CalibrationLineTool);
392
602
  (0,dist_esm.addTool)(dist_esm.TrackballRotateTool);
603
+ (0,dist_esm.addTool)(dist_esm.CircleScissorsTool);
604
+ (0,dist_esm.addTool)(dist_esm.RectangleScissorsTool);
605
+ (0,dist_esm.addTool)(dist_esm.SphereScissorsTool);
606
+ (0,dist_esm.addTool)(tools_ImageOverlayViewerTool);
393
607
 
394
608
  // Modify annotation tools to use dashed lines on SR
395
609
  const annotationStyle = {
@@ -428,13 +642,17 @@ const toolNames = {
428
642
  SegmentationDisplay: dist_esm.SegmentationDisplayTool.toolName,
429
643
  ReferenceLines: dist_esm.ReferenceLinesTool.toolName,
430
644
  CalibrationLine: tools_CalibrationLineTool.toolName,
431
- TrackballRotateTool: dist_esm.TrackballRotateTool.toolName
645
+ TrackballRotateTool: dist_esm.TrackballRotateTool.toolName,
646
+ CircleScissors: dist_esm.CircleScissorsTool.toolName,
647
+ RectangleScissors: dist_esm.RectangleScissorsTool.toolName,
648
+ SphereScissors: dist_esm.SphereScissorsTool.toolName,
649
+ ImageOverlayViewer: tools_ImageOverlayViewerTool.toolName
432
650
  };
433
651
 
434
652
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js
435
653
  /* harmony default export */ const supportedTools = (['Length', 'EllipticalROI', 'CircleROI', 'Bidirectional', 'ArrowAnnotate', 'Angle', 'CobbAngle', 'Probe', 'RectangleROI', 'PlanarFreehandROI']);
436
654
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js
437
- var getSOPInstanceAttributes = __webpack_require__(63130);
655
+ var getSOPInstanceAttributes = __webpack_require__(87172);
438
656
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts
439
657
 
440
658
 
@@ -537,9 +755,9 @@ function getMappedAnnotations(annotation, displaySetService) {
537
755
  SeriesNumber
538
756
  } = displaySet;
539
757
  const {
540
- length
758
+ length,
759
+ unit = 'mm'
541
760
  } = targetStats;
542
- const unit = 'mm';
543
761
  annotations.push({
544
762
  SeriesInstanceUID,
545
763
  SOPInstanceUID,
@@ -566,10 +784,13 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
566
784
  values.push('Cornerstone:Length');
567
785
  mappedAnnotations.forEach(annotation => {
568
786
  const {
569
- length
787
+ length,
788
+ unit
570
789
  } = annotation;
571
- columns.push(`Length (mm)`);
790
+ columns.push(`Length`);
572
791
  values.push(length);
792
+ columns.push('Unit');
793
+ values.push(unit);
573
794
  });
574
795
  if (FrameOfReferenceUID) {
575
796
  columns.push('FrameOfReferenceUID');
@@ -598,7 +819,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
598
819
  length,
599
820
  SeriesNumber,
600
821
  SOPInstanceUID,
601
- frameNumber
822
+ frameNumber,
823
+ unit
602
824
  } = mappedAnnotations[0];
603
825
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
604
826
  let InstanceNumber;
@@ -607,9 +829,11 @@ function getDisplayText(mappedAnnotations, displaySet) {
607
829
  }
608
830
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
609
831
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
610
- if (length === null || length === undefined) return displayText;
832
+ if (length === null || length === undefined) {
833
+ return displayText;
834
+ }
611
835
  const roundedLength = src.utils.roundNumber(length, 2);
612
- displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
836
+ displayText.push(`${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
613
837
  return displayText;
614
838
  }
615
839
  /* harmony default export */ const measurementServiceMappings_Length = (Length);
@@ -711,9 +935,9 @@ function Bidirectional_getMappedAnnotations(annotation, displaySetService) {
711
935
  } = displaySet;
712
936
  const {
713
937
  length,
714
- width
938
+ width,
939
+ unit
715
940
  } = targetStats;
716
- const unit = 'mm';
717
941
  annotations.push({
718
942
  SeriesInstanceUID,
719
943
  SOPInstanceUID,
@@ -742,10 +966,11 @@ function Bidirectional_getReport(mappedAnnotations, points, FrameOfReferenceUID)
742
966
  mappedAnnotations.forEach(annotation => {
743
967
  const {
744
968
  length,
745
- width
969
+ width,
970
+ unit
746
971
  } = annotation;
747
- columns.push(`Length (mm)`, `Width (mm)`);
748
- values.push(length, width);
972
+ columns.push(`Length`, `Width`, 'Unit');
973
+ values.push(length, width, unit);
749
974
  });
750
975
  if (FrameOfReferenceUID) {
751
976
  columns.push('FrameOfReferenceUID');
@@ -773,6 +998,7 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
773
998
  const {
774
999
  length,
775
1000
  width,
1001
+ unit,
776
1002
  SeriesNumber,
777
1003
  SOPInstanceUID,
778
1004
  frameNumber
@@ -786,27 +1012,15 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
786
1012
  }
787
1013
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
788
1014
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
789
- displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
790
- displayText.push(`W: ${roundedWidth} mm`);
1015
+ displayText.push(`L: ${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1016
+ displayText.push(`W: ${roundedWidth} ${unit}`);
791
1017
  return displayText;
792
1018
  }
793
1019
  /* 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
1020
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts
806
1021
 
807
1022
 
808
1023
 
809
-
810
1024
  const EllipticalROI = {
811
1025
  toAnnotation: measurement => {},
812
1026
  toMeasurement: (csToolsEventDetail, displaySetService, cornerstoneViewportService, getValueTypeFromToolType) => {
@@ -904,16 +1118,18 @@ function EllipticalROI_getMappedAnnotations(annotation, displaySetService) {
904
1118
  stdDev,
905
1119
  max,
906
1120
  area,
907
- Modality
1121
+ Modality,
1122
+ areaUnit,
1123
+ modalityUnit
908
1124
  } = targetStats;
909
- const unit = utils_getModalityUnit(Modality);
910
1125
  annotations.push({
911
1126
  SeriesInstanceUID,
912
1127
  SOPInstanceUID,
913
1128
  SeriesNumber,
914
1129
  frameNumber,
915
1130
  Modality,
916
- unit,
1131
+ unit: modalityUnit,
1132
+ areaUnit,
917
1133
  mean,
918
1134
  stdDev,
919
1135
  max,
@@ -941,13 +1157,14 @@ function EllipticalROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
941
1157
  stdDev,
942
1158
  max,
943
1159
  area,
944
- unit
1160
+ unit,
1161
+ areaUnit
945
1162
  } = annotation;
946
1163
  if (!mean || !unit || !max || !area) {
947
1164
  return;
948
1165
  }
949
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
950
- values.push(max, mean, stdDev, area);
1166
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1167
+ values.push(max, mean, stdDev, area, areaUnit);
951
1168
  });
952
1169
  if (FrameOfReferenceUID) {
953
1170
  columns.push('FrameOfReferenceUID');
@@ -975,7 +1192,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
975
1192
  const {
976
1193
  area,
977
1194
  SOPInstanceUID,
978
- frameNumber
1195
+ frameNumber,
1196
+ areaUnit
979
1197
  } = mappedAnnotations[0];
980
1198
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
981
1199
  let InstanceNumber;
@@ -984,10 +1202,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
984
1202
  }
985
1203
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
986
1204
  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>`);
1205
+ const roundedArea = src.utils.roundNumber(area, 2);
1206
+ displayText.push(`${roundedArea} ${areaUnit}`);
991
1207
 
992
1208
  // Todo: we need a better UI for displaying all these information
993
1209
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -1013,7 +1229,6 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
1013
1229
 
1014
1230
 
1015
1231
 
1016
-
1017
1232
  const CircleROI = {
1018
1233
  toAnnotation: measurement => {},
1019
1234
  toMeasurement: (csToolsEventDetail, DisplaySetService, CornerstoneViewportService, getValueTypeFromToolType) => {
@@ -1111,20 +1326,22 @@ function CircleROI_getMappedAnnotations(annotation, DisplaySetService) {
1111
1326
  stdDev,
1112
1327
  max,
1113
1328
  area,
1114
- Modality
1329
+ Modality,
1330
+ areaUnit,
1331
+ modalityUnit
1115
1332
  } = targetStats;
1116
- const unit = utils_getModalityUnit(Modality);
1117
1333
  annotations.push({
1118
1334
  SeriesInstanceUID,
1119
1335
  SOPInstanceUID,
1120
1336
  SeriesNumber,
1121
1337
  frameNumber,
1122
1338
  Modality,
1123
- unit,
1339
+ unit: modalityUnit,
1124
1340
  mean,
1125
1341
  stdDev,
1126
1342
  max,
1127
- area
1343
+ area,
1344
+ areaUnit
1128
1345
  });
1129
1346
  });
1130
1347
  return annotations;
@@ -1148,13 +1365,14 @@ function CircleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID) {
1148
1365
  stdDev,
1149
1366
  max,
1150
1367
  area,
1151
- unit
1368
+ unit,
1369
+ areaUnit
1152
1370
  } = annotation;
1153
1371
  if (!mean || !unit || !max || !area) {
1154
1372
  return;
1155
1373
  }
1156
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1157
- values.push(max, mean, stdDev, area);
1374
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1375
+ values.push(max, mean, stdDev, area, areaUnit);
1158
1376
  });
1159
1377
  if (FrameOfReferenceUID) {
1160
1378
  columns.push('FrameOfReferenceUID');
@@ -1182,7 +1400,8 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1182
1400
  const {
1183
1401
  area,
1184
1402
  SOPInstanceUID,
1185
- frameNumber
1403
+ frameNumber,
1404
+ areaUnit
1186
1405
  } = mappedAnnotations[0];
1187
1406
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
1188
1407
  let InstanceNumber;
@@ -1194,7 +1413,7 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1194
1413
 
1195
1414
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
1196
1415
  const roundedArea = src.utils.roundNumber(area || 0, 2);
1197
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
1416
+ displayText.push(`${roundedArea} ${areaUnit}`);
1198
1417
 
1199
1418
  // Todo: we need a better UI for displaying all these information
1200
1419
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -1515,7 +1734,9 @@ function CobbAngle_getDisplayText(mappedAnnotations, displaySet) {
1515
1734
  }
1516
1735
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1517
1736
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1518
- if (angle === undefined) return displayText;
1737
+ if (angle === undefined) {
1738
+ return displayText;
1739
+ }
1519
1740
  const roundedAngle = src.utils.roundNumber(angle, 2);
1520
1741
  displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1521
1742
  return displayText;
@@ -1695,7 +1916,9 @@ function Angle_getDisplayText(mappedAnnotations, displaySet) {
1695
1916
  }
1696
1917
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1697
1918
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1698
- if (angle === undefined) return displayText;
1919
+ if (angle === undefined) {
1920
+ return displayText;
1921
+ }
1699
1922
  const roundedAngle = src.utils.roundNumber(angle, 2);
1700
1923
  displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1701
1924
  return displayText;
@@ -1839,7 +2062,6 @@ function PlanarFreehandROI_getDisplayText(mappedAnnotations) {
1839
2062
 
1840
2063
 
1841
2064
 
1842
-
1843
2065
  const RectangleROI = {
1844
2066
  toAnnotation: measurement => {},
1845
2067
  toMeasurement: (csToolsEventDetail, DisplaySetService, CornerstoneViewportService, getValueTypeFromToolType) => {
@@ -1937,20 +2159,22 @@ function RectangleROI_getMappedAnnotations(annotation, DisplaySetService) {
1937
2159
  stdDev,
1938
2160
  max,
1939
2161
  area,
1940
- Modality
2162
+ Modality,
2163
+ modalityUnit,
2164
+ areaUnit
1941
2165
  } = targetStats;
1942
- const unit = utils_getModalityUnit(Modality);
1943
2166
  annotations.push({
1944
2167
  SeriesInstanceUID,
1945
2168
  SOPInstanceUID,
1946
2169
  SeriesNumber,
1947
2170
  frameNumber,
1948
2171
  Modality,
1949
- unit,
2172
+ unit: modalityUnit,
1950
2173
  mean,
1951
2174
  stdDev,
1952
2175
  max,
1953
- area
2176
+ area,
2177
+ areaUnit
1954
2178
  });
1955
2179
  });
1956
2180
  return annotations;
@@ -1974,13 +2198,14 @@ function RectangleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
1974
2198
  stdDev,
1975
2199
  max,
1976
2200
  area,
1977
- unit
2201
+ unit,
2202
+ areaUnit
1978
2203
  } = annotation;
1979
2204
  if (!mean || !unit || !max || !area) {
1980
2205
  return;
1981
2206
  }
1982
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1983
- values.push(max, mean, stdDev, area);
2207
+ columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit');
2208
+ values.push(max, mean, stdDev, unit, area, areaUnit);
1984
2209
  });
1985
2210
  if (FrameOfReferenceUID) {
1986
2211
  columns.push('FrameOfReferenceUID');
@@ -2008,7 +2233,8 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2008
2233
  const {
2009
2234
  area,
2010
2235
  SOPInstanceUID,
2011
- frameNumber
2236
+ frameNumber,
2237
+ areaUnit
2012
2238
  } = mappedAnnotations[0];
2013
2239
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
2014
2240
  let InstanceNumber;
@@ -2020,7 +2246,7 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2020
2246
 
2021
2247
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
2022
2248
  const roundedArea = src.utils.roundNumber(area || 0, 2);
2023
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
2249
+ displayText.push(`${roundedArea} ${areaUnit}`);
2024
2250
 
2025
2251
  // Todo: we need a better UI for displaying all these information
2026
2252
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -2515,7 +2741,7 @@ function getInterleavedFrames(imageIds) {
2515
2741
  }
2516
2742
  while (!prefetchQueuedFilled.currentPositionDownToMinimum || !prefetchQueuedFilled.currentPositionUpToMaximum) {
2517
2743
  if (!prefetchQueuedFilled.currentPositionDownToMinimum) {
2518
- // Add imageId bellow
2744
+ // Add imageId below
2519
2745
  lowerImageIdIndex--;
2520
2746
  imageIdsToPrefetch.push({
2521
2747
  imageId: imageIds[lowerImageIdIndex],
@@ -2540,7 +2766,7 @@ function getInterleavedFrames(imageIds) {
2540
2766
  return imageIdsToPrefetch;
2541
2767
  }
2542
2768
  // EXTERNAL MODULE: ../../../node_modules/lodash/lodash.js
2543
- var lodash = __webpack_require__(92891);
2769
+ var lodash = __webpack_require__(44379);
2544
2770
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/interleaveCenterLoader.ts
2545
2771
 
2546
2772
 
@@ -2730,8 +2956,12 @@ function getNthFrames(imageIds) {
2730
2956
  * @returns [] reordered to be breadth first traversal of lists
2731
2957
  */
2732
2958
  function interleave(lists) {
2733
- if (!lists || !lists.length) return [];
2734
- if (lists.length === 1) return lists[0];
2959
+ if (!lists || !lists.length) {
2960
+ return [];
2961
+ }
2962
+ if (lists.length === 1) {
2963
+ return lists[0];
2964
+ }
2735
2965
  console.time('interleave');
2736
2966
  const useLists = [...lists];
2737
2967
  const ret = [];
@@ -3004,6 +3234,7 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3004
3234
  commands: [{
3005
3235
  commandName: 'showCornerstoneContextMenu',
3006
3236
  commandOptions: {
3237
+ requireNearbyToolData: true,
3007
3238
  menuId: 'measurementsContextMenu'
3008
3239
  }
3009
3240
  }]
@@ -3020,9 +3251,15 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3020
3251
  function getEventName(evt) {
3021
3252
  const button = evt.detail.event.which;
3022
3253
  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');
3254
+ if (evt.detail.event.altKey) {
3255
+ nameArr.push('alt');
3256
+ }
3257
+ if (evt.detail.event.ctrlKey) {
3258
+ nameArr.push('ctrl');
3259
+ }
3260
+ if (evt.detail.event.shiftKey) {
3261
+ nameArr.push('shift');
3262
+ }
3026
3263
  nameArr.push('button');
3027
3264
  nameArr.push(button);
3028
3265
  return nameArr.join('');
@@ -3040,9 +3277,18 @@ function initContextMenu(_ref) {
3040
3277
  const cornerstoneViewportHandleEvent = (name, evt) => {
3041
3278
  const customizations = customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
3042
3279
  const toRun = customizations[name];
3043
- console.log('initContextMenu::cornerstoneViewportHandleEvent', name, toRun);
3280
+ if (!toRun) {
3281
+ return;
3282
+ }
3283
+
3284
+ // only find nearbyToolData if required, for the click (which closes the context menu
3285
+ // we don't need to find nearbyToolData)
3286
+ let nearbyToolData = null;
3287
+ if (toRun.commands.some(command => command.commandOptions?.requireNearbyToolData)) {
3288
+ nearbyToolData = findNearbyToolData(commandsManager, evt);
3289
+ }
3044
3290
  const options = {
3045
- nearbyToolData: findNearbyToolData(commandsManager, evt),
3291
+ nearbyToolData,
3046
3292
  event: evt
3047
3293
  };
3048
3294
  commandsManager.run(toRun, options);
@@ -3057,10 +3303,11 @@ function initContextMenu(_ref) {
3057
3303
  element
3058
3304
  } = evt.detail;
3059
3305
  const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
3060
- if (!viewportInfo) return;
3061
- const viewportIndex = viewportInfo.getViewportIndex();
3306
+ if (!viewportInfo) {
3307
+ return;
3308
+ }
3062
3309
  // TODO check update upstream
3063
- (0,state/* setEnabledElement */.Yc)(viewportIndex, element);
3310
+ (0,state/* setEnabledElement */.Yc)(viewportId, element);
3064
3311
  element.addEventListener(cs3DToolsEvents.MOUSE_CLICK, cornerstoneViewportHandleClick);
3065
3312
  }
3066
3313
  function elementDisabledHandler(evt) {
@@ -3094,9 +3341,15 @@ const DEFAULT_DOUBLE_CLICK = {
3094
3341
  */
3095
3342
  function getDoubleClickEventName(evt) {
3096
3343
  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');
3344
+ if (evt.detail.event.altKey) {
3345
+ nameArr.push('alt');
3346
+ }
3347
+ if (evt.detail.event.ctrlKey) {
3348
+ nameArr.push('ctrl');
3349
+ }
3350
+ if (evt.detail.event.shiftKey) {
3351
+ nameArr.push('shift');
3352
+ }
3100
3353
  nameArr.push('doubleClick');
3101
3354
  return nameArr.join('');
3102
3355
  }
@@ -3154,7 +3407,6 @@ function initDoubleClick(_ref) {
3154
3407
 
3155
3408
 
3156
3409
 
3157
-
3158
3410
  // TODO: Cypress tests are currently grabbing this from the window?
3159
3411
  window.cornerstone = esm;
3160
3412
  window.cornerstoneTools = dist_esm;
@@ -3169,10 +3421,25 @@ async function init(_ref) {
3169
3421
  configuration,
3170
3422
  appConfig
3171
3423
  } = _ref;
3172
- await (0,esm.init)();
3424
+ await (0,esm.init)({
3425
+ rendering: {
3426
+ preferSizeOverAccuracy: Boolean(appConfig.use16BitDataType),
3427
+ useNorm16Texture: Boolean(appConfig.use16BitDataType)
3428
+ }
3429
+ });
3173
3430
 
3174
3431
  // For debugging e2e tests that are failing on CI
3175
3432
  esm.setUseCPURendering(Boolean(appConfig.useCPURendering));
3433
+ switch (appConfig.useSharedArrayBuffer) {
3434
+ case 'AUTO':
3435
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.AUTO);
3436
+ break;
3437
+ case 'FALSE':
3438
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.FALSE);
3439
+ break;
3440
+ default:
3441
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.TRUE);
3442
+ }
3176
3443
  esm.setConfiguration({
3177
3444
  ...esm.getConfiguration(),
3178
3445
  rendering: {
@@ -3181,18 +3448,18 @@ async function init(_ref) {
3181
3448
  }
3182
3449
  });
3183
3450
 
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);
3451
+ // For debugging large datasets, otherwise prefer the defaults
3452
+ const {
3453
+ maxCacheSize
3454
+ } = appConfig;
3455
+ if (maxCacheSize) {
3456
+ esm.cache.setMaxCacheSize(maxCacheSize);
3457
+ }
3188
3458
  initCornerstoneTools();
3189
3459
  esm.Settings.getRuntimeSettings().set('useCursors', Boolean(appConfig.useCursors));
3190
3460
  const {
3191
3461
  userAuthenticationService,
3192
- measurementService,
3193
3462
  customizationService,
3194
- displaySetService,
3195
- uiDialogService,
3196
3463
  uiModalService,
3197
3464
  uiNotificationService,
3198
3465
  cineService,
@@ -3254,7 +3521,7 @@ async function init(_ref) {
3254
3521
  thumbnail: appConfig?.maxNumRequests?.thumbnail || 75,
3255
3522
  prefetch: appConfig?.maxNumRequests?.prefetch || 10
3256
3523
  };
3257
- initWADOImageLoader(userAuthenticationService, appConfig);
3524
+ initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
3258
3525
 
3259
3526
  /* Measurement Service */
3260
3527
  this.measurementServiceSource = connectToolsToMeasurementService(servicesManager);
@@ -3338,12 +3605,10 @@ async function init(_ref) {
3338
3605
 
3339
3606
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
3340
3607
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
3341
- viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_INDEX_CHANGED, _ref2 => {
3608
+ viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, _ref2 => {
3342
3609
  let {
3343
- viewportIndex,
3344
3610
  viewportId
3345
3611
  } = _ref2;
3346
- viewportId = viewportId || `viewport-${viewportIndex}`;
3347
3612
  const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
3348
3613
  if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
3349
3614
  return;
@@ -3387,12 +3652,12 @@ function _showCPURenderingModal(uiModalService, hangingProtocolService) {
3387
3652
  });
3388
3653
  }
3389
3654
  // EXTERNAL MODULE: ../../../node_modules/react-dropzone/dist/es/index.js + 5 modules
3390
- var es = __webpack_require__(58591);
3655
+ var es = __webpack_require__(74834);
3391
3656
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
3392
- var prop_types = __webpack_require__(60216);
3657
+ var prop_types = __webpack_require__(3827);
3393
3658
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
3394
3659
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
3395
- var classnames = __webpack_require__(40841);
3660
+ var classnames = __webpack_require__(44921);
3396
3661
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
3397
3662
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/DicomFileUploader.ts
3398
3663
 
@@ -3541,7 +3806,9 @@ class DicomFileUploader extends src/* PubSubService */.hC {
3541
3806
  request.addEventListener('loadend', cleanUpCallback);
3542
3807
  }
3543
3808
  _checkDicomFile(arrayBuffer) {
3544
- if (arrayBuffer.length <= 132) return false;
3809
+ if (arrayBuffer.length <= 132) {
3810
+ return false;
3811
+ }
3545
3812
  const arr = new Uint8Array(arrayBuffer.slice(128, 132));
3546
3813
  // bytes from 128 to 132 must be "DICM"
3547
3814
  return Array.from('DICM').every((char, i) => char.charCodeAt(0) === arr[i]);
@@ -3560,7 +3827,6 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3560
3827
  const [percentComplete, setPercentComplete] = (0,react.useState)(dicomFileUploader.getPercentComplete());
3561
3828
  const [failedReason, setFailedReason] = (0,react.useState)('');
3562
3829
  const [status, setStatus] = (0,react.useState)(dicomFileUploader.getStatus());
3563
- console.info(`${dicomFileUploader.getFileId()}`);
3564
3830
  const isComplete = (0,react.useCallback)(() => {
3565
3831
  return status === UploadStatus.Failed || status === UploadStatus.Cancelled || status === UploadStatus.Success;
3566
3832
  }, [status]);
@@ -3601,25 +3867,25 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3601
3867
  }
3602
3868
  };
3603
3869
  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"
3870
+ className: "min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg"
3605
3871
  }, /*#__PURE__*/react.createElement("div", {
3606
- className: "flex flex-col gap-1 self-top w-0 grow shrink"
3872
+ className: "self-top flex w-0 shrink grow flex-col gap-1"
3607
3873
  }, /*#__PURE__*/react.createElement("div", {
3608
3874
  className: "flex gap-4"
3609
3875
  }, /*#__PURE__*/react.createElement("div", {
3610
- className: "flex w-6 justify-center items-center shrink-0"
3876
+ className: "flex w-6 shrink-0 items-center justify-center"
3611
3877
  }, getStatusIcon()), /*#__PURE__*/react.createElement("div", {
3612
- className: "text-ellipsis whitespace-nowrap overflow-hidden"
3878
+ className: "overflow-hidden text-ellipsis whitespace-nowrap"
3613
3879
  }, dicomFileUploader.getFileName())), failedReason && /*#__PURE__*/react.createElement("div", {
3614
3880
  className: "pl-10"
3615
3881
  }, failedReason)), /*#__PURE__*/react.createElement("div", {
3616
- className: "w-24 flex items-center"
3882
+ className: "flex w-24 items-center"
3617
3883
  }, !isComplete() && /*#__PURE__*/react.createElement(react.Fragment, null, dicomFileUploader.getStatus() === UploadStatus.InProgress && /*#__PURE__*/react.createElement("div", {
3618
3884
  className: "w-10 text-right"
3619
3885
  }, percentComplete, "%"), /*#__PURE__*/react.createElement("div", {
3620
- className: "flex cursor-pointer ml-auto"
3886
+ className: "ml-auto flex cursor-pointer"
3621
3887
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
3622
- className: "self-center text-primary-active",
3888
+ className: "text-primary-active self-center",
3623
3889
  name: "close",
3624
3890
  onClick: cancelUpload
3625
3891
  })))));
@@ -3846,7 +4112,7 @@ function DicomUploadProgress(_ref) {
3846
4112
  }, []);
3847
4113
  const getNumCompletedAndTimeRemainingComponent = () => {
3848
4114
  return /*#__PURE__*/react.createElement("div", {
3849
- className: "text-lg px-1 pb-4 h-14 flex bg-primary-dark items-center"
4115
+ className: "bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg"
3850
4116
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("span", {
3851
4117
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3852
4118
  }, `${dicomFileUploaderArr.length} ${dicomFileUploaderArr.length > 1 ? 'files' : 'file'} completed.`), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
@@ -3861,13 +4127,13 @@ function DicomUploadProgress(_ref) {
3861
4127
  }, ' files completed.', "\xA0"), /*#__PURE__*/react.createElement("span", {
3862
4128
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3863
4129
  }, 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'),
4130
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES, 'text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'),
3865
4131
  onClick: cancelAllUploads
3866
4132
  }, "Cancel All Uploads")));
3867
4133
  };
3868
4134
  const getShowFailedOnlyIconComponent = () => {
3869
4135
  return /*#__PURE__*/react.createElement("div", {
3870
- className: "ml-auto flex justify-center w-6"
4136
+ className: "ml-auto flex w-6 justify-center"
3871
4137
  }, numFails > 0 && /*#__PURE__*/react.createElement("div", {
3872
4138
  onClick: () => setShowFailedOnly(currentShowFailedOnly => !currentShowFailedOnly)
3873
4139
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
@@ -3877,28 +4143,28 @@ function DicomUploadProgress(_ref) {
3877
4143
  };
3878
4144
  const getPercentCompleteComponent = () => {
3879
4145
  return /*#__PURE__*/react.createElement("div", {
3880
- className: "overflow-y-scroll ohif-scrollbar px-2 border-b border-secondary-light"
4146
+ className: "ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2"
3881
4147
  }, /*#__PURE__*/react.createElement("div", {
3882
- className: "flex w-full p-2.5 items-center min-h-14"
4148
+ className: "min-h-14 flex w-full items-center p-2.5"
3883
4149
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3884
- className: "text-xl text-primary-light"
4150
+ className: "text-primary-light text-xl"
3885
4151
  }, numFails > 0 ? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!` : 'Completed!'), getShowFailedOnlyIconComponent()) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3886
4152
  ref: progressBarContainerRef,
3887
4153
  className: "flex-grow"
3888
4154
  }, /*#__PURE__*/react.createElement(ui_src/* ProgressLoadingBar */.YE, {
3889
4155
  progress: showInfiniteProgressBar() ? undefined : Math.min(100, percentComplete)
3890
4156
  })), /*#__PURE__*/react.createElement("div", {
3891
- className: "w-24 ml-1 flex items-center"
4157
+ className: "ml-1 flex w-24 items-center"
3892
4158
  }, /*#__PURE__*/react.createElement("div", {
3893
4159
  className: "w-10 text-right"
3894
4160
  }, `${getPercentCompleteRounded()}%`), getShowFailedOnlyIconComponent()))));
3895
4161
  };
3896
4162
  return /*#__PURE__*/react.createElement("div", {
3897
- className: "flex flex-col grow"
4163
+ className: "flex grow flex-col"
3898
4164
  }, getNumCompletedAndTimeRemainingComponent(), /*#__PURE__*/react.createElement("div", {
3899
- className: "flex flex-col bg-black text-lg overflow-hidden grow"
4165
+ className: "flex grow flex-col overflow-hidden bg-black text-lg"
3900
4166
  }, getPercentCompleteComponent(), /*#__PURE__*/react.createElement("div", {
3901
- className: "overflow-y-scroll ohif-scrollbar px-2 grow h-1"
4167
+ className: "ohif-scrollbar h-1 grow overflow-y-scroll px-2"
3902
4168
  }, dicomFileUploaderArr.filter(dicomFileUploader => !showFailedOnly || dicomFileUploader.getStatus() === UploadStatus.Failed).map(dicomFileUploader => /*#__PURE__*/react.createElement(DicomUpload_DicomUploadProgressItem, {
3903
4169
  key: dicomFileUploader.getFileId(),
3904
4170
  dicomFileUploader: dicomFileUploader
@@ -3945,7 +4211,7 @@ function DicomUpload(_ref) {
3945
4211
  getRootProps
3946
4212
  } = _ref2;
3947
4213
  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"
4214
+ className: "dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
3949
4215
  }), /*#__PURE__*/react.createElement("div", {
3950
4216
  className: "flex gap-3"
3951
4217
  }, /*#__PURE__*/react.createElement(es/* default */.Z, {
@@ -3969,7 +4235,7 @@ function DicomUpload(_ref) {
3969
4235
  getInputProps
3970
4236
  } = _ref4;
3971
4237
  return /*#__PURE__*/react.createElement("div", getRootProps(), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
3972
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary,
4238
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary,
3973
4239
  disabled: false,
3974
4240
  onClick: () => {}
3975
4241
  }, 'Add folder', /*#__PURE__*/react.createElement("input", _extends({}, getInputProps(), {
@@ -3979,7 +4245,7 @@ function DicomUpload(_ref) {
3979
4245
  })), /*#__PURE__*/react.createElement("div", {
3980
4246
  className: "pt-5"
3981
4247
  }, "or drag images or folders here"), /*#__PURE__*/react.createElement("div", {
3982
- className: "pt-3 text-aqua-pale text-lg"
4248
+ className: "text-aqua-pale pt-3 text-lg"
3983
4249
  }, "(DICOM files supported)"));
3984
4250
  });
3985
4251
  };
@@ -4043,7 +4309,7 @@ function getCustomizationModule() {
4043
4309
  }
4044
4310
  /* harmony default export */ const src_getCustomizationModule = (getCustomizationModule);
4045
4311
  // EXTERNAL MODULE: ../../../node_modules/html2canvas/dist/html2canvas.esm.js
4046
- var html2canvas_esm = __webpack_require__(63691);
4312
+ var html2canvas_esm = __webpack_require__(76010);
4047
4313
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
4048
4314
 
4049
4315
 
@@ -4059,10 +4325,10 @@ const VIEWPORT_ID = 'cornerstone-viewport-download-form';
4059
4325
  const CornerstoneViewportDownloadForm = _ref => {
4060
4326
  let {
4061
4327
  onClose,
4062
- activeViewportIndex,
4328
+ activeViewportId: activeViewportIdProp,
4063
4329
  cornerstoneViewportService
4064
4330
  } = _ref;
4065
- const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIndex);
4331
+ const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIdProp);
4066
4332
  const activeViewportElement = enabledElement?.element;
4067
4333
  const activeViewportEnabledElement = (0,esm.getEnabledElement)(activeViewportElement);
4068
4334
  const {
@@ -4264,7 +4530,7 @@ const CornerstoneViewportDownloadForm = _ref => {
4264
4530
  };
4265
4531
  CornerstoneViewportDownloadForm.propTypes = {
4266
4532
  onClose: (prop_types_default()).func,
4267
- activeViewportIndex: (prop_types_default()).number.isRequired
4533
+ activeViewportId: (prop_types_default()).string.isRequired
4268
4534
  };
4269
4535
  /* harmony default export */ const utils_CornerstoneViewportDownloadForm = (CornerstoneViewportDownloadForm);
4270
4536
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/stackSync/calculateViewportRegistrations.ts
@@ -4294,7 +4560,7 @@ const _getViewportPairs = viewports => {
4294
4560
 
4295
4561
  // [ {
4296
4562
  // synchronizerId: string,
4297
- // viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
4563
+ // viewports: [ { viewportId: string, renderingEngineId: string, index: number } , ...]
4298
4564
  // ]}
4299
4565
  let STACK_IMAGE_SYNC_GROUPS_INFO = [];
4300
4566
  function toggleStackImageSync(_ref) {
@@ -4328,27 +4594,23 @@ function toggleStackImageSync(_ref) {
4328
4594
  STACK_IMAGE_SYNC_GROUPS_INFO = [];
4329
4595
 
4330
4596
  // create synchronization groups and add viewports
4331
- let {
4597
+ const {
4332
4598
  viewports
4333
4599
  } = viewportGridService.getState();
4334
4600
 
4335
4601
  // filter empty viewports
4336
- viewports = viewports.filter(viewport => viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length);
4337
-
4602
+ const viewportsArray = Array.from(viewports.values()).filter(viewport => viewport.displaySetInstanceUIDs?.length)
4338
4603
  // filter reconstructable viewports
4339
- viewports = viewports.filter(viewport => {
4604
+ .filter(viewport => {
4340
4605
  const {
4341
4606
  displaySetInstanceUIDs
4342
4607
  } = viewport;
4343
4608
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
4344
4609
  const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
4345
- if (displaySet && displaySet.isReconstructable) {
4346
- return true;
4347
- }
4348
- return false;
4610
+ return !!displaySet?.isReconstructable;
4349
4611
  }
4350
4612
  });
4351
- const viewportsByOrientation = viewports.reduce((acc, viewport) => {
4613
+ const viewportsByOrientation = viewportsArray.reduce((acc, viewport) => {
4352
4614
  const {
4353
4615
  viewportId,
4354
4616
  viewportType
@@ -4457,6 +4719,7 @@ function commandsModule(_ref) {
4457
4719
  toolGroupService,
4458
4720
  cineService,
4459
4721
  toolbarService,
4722
+ stateSyncService,
4460
4723
  uiDialogService,
4461
4724
  cornerstoneViewportService,
4462
4725
  uiNotificationService,
@@ -4650,8 +4913,7 @@ function commandsModule(_ref) {
4650
4913
  console.warn('No viewport found for viewportId:', viewportId);
4651
4914
  return;
4652
4915
  }
4653
- const viewportIndex = viewportInfo.getViewportIndex();
4654
- viewportGridService.setActiveViewportIndex(viewportIndex);
4916
+ viewportGridService.setActiveViewportId(viewportId);
4655
4917
  },
4656
4918
  arrowTextCallback: _ref7 => {
4657
4919
  let {
@@ -4719,7 +4981,8 @@ function commandsModule(_ref) {
4719
4981
  setToolActive: _ref9 => {
4720
4982
  let {
4721
4983
  toolName,
4722
- toolGroupId = null
4984
+ toolGroupId = null,
4985
+ toggledState
4723
4986
  } = _ref9;
4724
4987
  if (toolName === 'Crosshairs') {
4725
4988
  const activeViewportToolGroup = toolGroupService.getToolGroup(null);
@@ -4735,9 +4998,10 @@ function commandsModule(_ref) {
4735
4998
  }
4736
4999
  const {
4737
5000
  viewports
4738
- } = viewportGridService.getState() || {
4739
- viewports: []
4740
- };
5001
+ } = viewportGridService.getState();
5002
+ if (!viewports.size) {
5003
+ return;
5004
+ }
4741
5005
  const toolGroup = toolGroupService.getToolGroup(toolGroupId);
4742
5006
  const toolGroupViewportIds = toolGroup?.getViewportIds?.();
4743
5007
 
@@ -4745,11 +5009,8 @@ function commandsModule(_ref) {
4745
5009
  if (!toolGroupViewportIds || !toolGroupViewportIds.length) {
4746
5010
  return;
4747
5011
  }
4748
- const filteredViewports = viewports.filter(viewport => {
4749
- if (!viewport.viewportOptions) {
4750
- return false;
4751
- }
4752
- return toolGroupViewportIds.includes(viewport.viewportOptions.viewportId);
5012
+ const filteredViewports = Array.from(viewports.values()).filter(viewport => {
5013
+ return toolGroupViewportIds.includes(viewport.viewportId);
4753
5014
  });
4754
5015
  if (!filteredViewports.length) {
4755
5016
  return;
@@ -4773,6 +5034,14 @@ function commandsModule(_ref) {
4773
5034
  toolGroup.setToolPassive(activeToolName);
4774
5035
  }
4775
5036
  }
5037
+
5038
+ // If there is a toggle state, then simply set the enabled/disabled state without
5039
+ // setting the tool active.
5040
+ if (toggledState != null) {
5041
+ toggledState ? toolGroup.setToolEnabled(toolName) : toolGroup.setToolDisabled(toolName);
5042
+ return;
5043
+ }
5044
+
4776
5045
  // Set the new toolName to be active
4777
5046
  toolGroup.setToolActive(toolName, {
4778
5047
  bindings: [{
@@ -4782,9 +5051,9 @@ function commandsModule(_ref) {
4782
5051
  },
4783
5052
  showDownloadViewportModal: () => {
4784
5053
  const {
4785
- activeViewportIndex
5054
+ activeViewportId
4786
5055
  } = viewportGridService.getState();
4787
- if (!cornerstoneViewportService.getCornerstoneViewportByIndex(activeViewportIndex)) {
5056
+ if (!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)) {
4788
5057
  // Cannot download a non-cornerstone viewport (image).
4789
5058
  uiNotificationService.show({
4790
5059
  title: 'Download Image',
@@ -4801,7 +5070,7 @@ function commandsModule(_ref) {
4801
5070
  content: utils_CornerstoneViewportDownloadForm,
4802
5071
  title: 'Download High Quality Image',
4803
5072
  contentProps: {
4804
- activeViewportIndex,
5073
+ activeViewportId,
4805
5074
  onClose: uiModalService.hide,
4806
5075
  cornerstoneViewportService
4807
5076
  }
@@ -4882,15 +5151,13 @@ function commandsModule(_ref) {
4882
5151
  const {
4883
5152
  viewport
4884
5153
  } = 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
- }
5154
+ const {
5155
+ invert
5156
+ } = viewport.getProperties();
5157
+ viewport.setProperties({
5158
+ invert: !invert
5159
+ });
5160
+ viewport.render();
4894
5161
  },
4895
5162
  resetViewport: () => {
4896
5163
  const enabledElement = _getActiveViewportEnabledElement();
@@ -4993,12 +5260,12 @@ function commandsModule(_ref) {
4993
5260
  },
4994
5261
  setViewportColormap: _ref15 => {
4995
5262
  let {
4996
- viewportIndex,
5263
+ viewportId,
4997
5264
  displaySetInstanceUID,
4998
5265
  colormap,
4999
5266
  immediate = false
5000
5267
  } = _ref15;
5001
- const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(viewportIndex);
5268
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
5002
5269
  const actorEntries = viewport.getActors();
5003
5270
  const actorEntry = actorEntries.find(actorEntry => {
5004
5271
  return actorEntry.uid.includes(displaySetInstanceUID);
@@ -5015,51 +5282,52 @@ function commandsModule(_ref) {
5015
5282
  viewport.render();
5016
5283
  }
5017
5284
  },
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: () => {
5285
+ changeActiveViewport: _ref16 => {
5286
+ let {
5287
+ direction = 1
5288
+ } = _ref16;
5027
5289
  const {
5028
- activeViewportIndex,
5290
+ activeViewportId,
5029
5291
  viewports
5030
5292
  } = viewportGridService.getState();
5031
- const nextViewportIndex = (activeViewportIndex - 1 + viewports.length) % viewports.length;
5032
- viewportGridService.setActiveViewportIndex(nextViewportIndex);
5293
+ const viewportIds = Array.from(viewports.keys());
5294
+ const currentIndex = viewportIds.indexOf(activeViewportId);
5295
+ const nextViewportIndex = (currentIndex + direction + viewportIds.length) % viewportIds.length;
5296
+ viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex]);
5033
5297
  },
5034
- toggleStackImageSync: _ref16 => {
5298
+ toggleStackImageSync: _ref17 => {
5035
5299
  let {
5036
5300
  toggledState
5037
- } = _ref16;
5301
+ } = _ref17;
5038
5302
  toggleStackImageSync({
5039
5303
  getEnabledElement: esm.getEnabledElement,
5040
5304
  servicesManager,
5041
5305
  toggledState
5042
5306
  });
5043
5307
  },
5044
- toggleReferenceLines: _ref17 => {
5308
+ setSourceViewportForReferenceLinesTool: _ref18 => {
5045
5309
  let {
5046
5310
  toggledState
5047
- } = _ref17;
5311
+ } = _ref18;
5048
5312
  const {
5049
- activeViewportIndex
5313
+ activeViewportId
5050
5314
  } = viewportGridService.getState();
5051
- const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(activeViewportIndex);
5315
+ const viewportInfo = cornerstoneViewportService.getViewportInfo(activeViewportId);
5052
5316
  const viewportId = viewportInfo.getViewportId();
5053
5317
  const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
5054
- if (!toggledState) {
5055
- toolGroup.setToolDisabled(dist_esm.ReferenceLinesTool.toolName);
5056
- }
5057
5318
  toolGroup.setToolConfiguration(dist_esm.ReferenceLinesTool.toolName, {
5058
5319
  sourceViewportId: viewportId
5059
5320
  }, true // overwrite
5060
5321
  );
5322
+ },
5061
5323
 
5062
- toolGroup.setToolEnabled(dist_esm.ReferenceLinesTool.toolName);
5324
+ storePresentation: _ref19 => {
5325
+ let {
5326
+ viewportId
5327
+ } = _ref19;
5328
+ cornerstoneViewportService.storePresentation({
5329
+ viewportId
5330
+ });
5063
5331
  }
5064
5332
  };
5065
5333
  const definitions = {
@@ -5114,10 +5382,13 @@ function commandsModule(_ref) {
5114
5382
  }
5115
5383
  },
5116
5384
  incrementActiveViewport: {
5117
- commandFn: actions.incrementActiveViewport
5385
+ commandFn: actions.changeActiveViewport
5118
5386
  },
5119
5387
  decrementActiveViewport: {
5120
- commandFn: actions.decrementActiveViewport
5388
+ commandFn: actions.changeActiveViewport,
5389
+ options: {
5390
+ direction: -1
5391
+ }
5121
5392
  },
5122
5393
  flipViewportHorizontal: {
5123
5394
  commandFn: actions.flipViewportHorizontal
@@ -5194,8 +5465,13 @@ function commandsModule(_ref) {
5194
5465
  toggleStackImageSync: {
5195
5466
  commandFn: actions.toggleStackImageSync
5196
5467
  },
5197
- toggleReferenceLines: {
5198
- commandFn: actions.toggleReferenceLines
5468
+ setSourceViewportForReferenceLinesTool: {
5469
+ commandFn: actions.setSourceViewportForReferenceLinesTool
5470
+ },
5471
+ storePresentation: {
5472
+ commandFn: actions.storePresentation,
5473
+ storeContexts: [],
5474
+ options: {}
5199
5475
  }
5200
5476
  };
5201
5477
  return {
@@ -5210,9 +5486,8 @@ const mpr = {
5210
5486
  id: 'mpr',
5211
5487
  name: 'Multi-Planar Reconstruction',
5212
5488
  locked: true,
5213
- hasUpdatedPriorsInformation: false,
5214
5489
  createdDate: '2021-02-23',
5215
- modifiedDate: '2023-04-03',
5490
+ modifiedDate: '2023-08-15',
5216
5491
  availableTo: {},
5217
5492
  editableBy: {},
5218
5493
  // Unknown number of priors referenced - so just match any study
@@ -5283,6 +5558,7 @@ const mpr = {
5283
5558
  },
5284
5559
  viewports: [{
5285
5560
  viewportOptions: {
5561
+ viewportId: 'mpr-axial',
5286
5562
  toolGroupId: 'mpr',
5287
5563
  viewportType: 'volume',
5288
5564
  orientation: 'axial',
@@ -5301,6 +5577,7 @@ const mpr = {
5301
5577
  }]
5302
5578
  }, {
5303
5579
  viewportOptions: {
5580
+ viewportId: 'mpr-sagittal',
5304
5581
  toolGroupId: 'mpr',
5305
5582
  viewportType: 'volume',
5306
5583
  orientation: 'sagittal',
@@ -5319,6 +5596,7 @@ const mpr = {
5319
5596
  }]
5320
5597
  }, {
5321
5598
  viewportOptions: {
5599
+ viewportId: 'mpr-coronal',
5322
5600
  toolGroupId: 'mpr',
5323
5601
  viewportType: 'volume',
5324
5602
  orientation: 'coronal',
@@ -5341,7 +5619,6 @@ const mpr = {
5341
5619
  const mprAnd3DVolumeViewport = {
5342
5620
  id: 'mprAnd3DVolumeViewport',
5343
5621
  locked: true,
5344
- hasUpdatedPriorsInformation: false,
5345
5622
  name: 'mpr',
5346
5623
  createdDate: '2023-03-15T10:29:44.894Z',
5347
5624
  modifiedDate: '2023-03-15T10:29:44.894Z',
@@ -5530,9 +5807,9 @@ class ToolGroupService {
5530
5807
  return dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngine.id);
5531
5808
  }
5532
5809
  getActiveToolForViewport(viewportId) {
5533
- const toolGroup = dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId);
5810
+ const toolGroup = this.getToolGroupForViewport(viewportId);
5534
5811
  if (!toolGroup) {
5535
- return null;
5812
+ return;
5536
5813
  }
5537
5814
  return toolGroup.getActivePrimaryMouseButtonTool();
5538
5815
  }
@@ -5595,9 +5872,8 @@ class ToolGroupService {
5595
5872
  this._setToolsMode(toolGroup, tools);
5596
5873
  }
5597
5874
  createToolGroupAndAddTools(toolGroupId, tools) {
5598
- let configs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5599
5875
  const toolGroup = this.createToolGroup(toolGroupId);
5600
- this.addToolsToToolGroup(toolGroupId, tools, configs);
5876
+ this.addToolsToToolGroup(toolGroupId, tools);
5601
5877
  return toolGroup;
5602
5878
  }
5603
5879
 
@@ -5643,30 +5919,6 @@ class ToolGroupService {
5643
5919
  const toolInstance = toolGroup.getToolInstance(toolName);
5644
5920
  toolInstance.configuration = config;
5645
5921
  }
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
5922
  _setToolsMode(toolGroup, tools) {
5671
5923
  const {
5672
5924
  active,
@@ -5710,29 +5962,46 @@ class ToolGroupService {
5710
5962
  });
5711
5963
  }
5712
5964
  }
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
5965
+ _addTools(toolGroup, tools) {
5966
+ const addTools = tools => {
5967
+ tools.forEach(_ref5 => {
5968
+ let {
5969
+ toolName,
5970
+ parentTool,
5971
+ configuration
5972
+ } = _ref5;
5973
+ if (parentTool) {
5974
+ toolGroup.addToolInstance(toolName, parentTool, {
5975
+ ...configuration
5976
+ });
5977
+ } else {
5978
+ toolGroup.addTool(toolName, {
5979
+ ...configuration
5980
+ });
5981
+ }
5725
5982
  });
5726
- });
5983
+ };
5984
+ if (tools.active) {
5985
+ addTools(tools.active);
5986
+ }
5987
+ if (tools.passive) {
5988
+ addTools(tools.passive);
5989
+ }
5990
+ if (tools.enabled) {
5991
+ addTools(tools.enabled);
5992
+ }
5993
+ if (tools.disabled) {
5994
+ addTools(tools.disabled);
5995
+ }
5727
5996
  }
5728
5997
  }
5729
5998
  ToolGroupService.REGISTRATION = {
5730
5999
  name: 'toolGroupService',
5731
6000
  altName: 'ToolGroupService',
5732
- create: _ref5 => {
6001
+ create: _ref6 => {
5733
6002
  let {
5734
6003
  servicesManager
5735
- } = _ref5;
6004
+ } = _ref6;
5736
6005
  return new ToolGroupService(servicesManager);
5737
6006
  }
5738
6007
  };
@@ -5789,7 +6058,7 @@ class SyncGroupService {
5789
6058
  * @param type is the type of the synchronizer to create
5790
6059
  * @param creator
5791
6060
  */
5792
- setSynchronizer(type, creator) {
6061
+ addSynchronizerType(type, creator) {
5793
6062
  this.synchronizerCreators[type.toLowerCase()] = creator;
5794
6063
  }
5795
6064
  _getOrCreateSynchronizer(type, id, options) {
@@ -5867,10 +6136,10 @@ SyncGroupService.REGISTRATION = {
5867
6136
 
5868
6137
  /* harmony default export */ const services_SyncGroupService = (SyncGroupService);
5869
6138
  // EXTERNAL MODULE: ../../../node_modules/lodash.clonedeep/index.js
5870
- var lodash_clonedeep = __webpack_require__(71975);
6139
+ var lodash_clonedeep = __webpack_require__(11677);
5871
6140
  var lodash_clonedeep_default = /*#__PURE__*/__webpack_require__.n(lodash_clonedeep);
5872
6141
  // EXTERNAL MODULE: ../../../node_modules/lodash.isequal/index.js
5873
- var lodash_isequal = __webpack_require__(68652);
6142
+ var lodash_isequal = __webpack_require__(10311);
5874
6143
  var lodash_isequal_default = /*#__PURE__*/__webpack_require__.n(lodash_isequal);
5875
6144
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/transitions.ts
5876
6145
  /**
@@ -6026,6 +6295,82 @@ class SegmentationService extends src/* PubSubService */.hC {
6026
6295
  segmentation
6027
6296
  });
6028
6297
  };
6298
+ // Todo: this should not run on the main thread
6299
+ this.calculateCentroids = (segmentationId, segmentIndex) => {
6300
+ const segmentation = this.getSegmentation(segmentationId);
6301
+ const volume = this.getLabelmapVolume(segmentationId);
6302
+ const {
6303
+ dimensions,
6304
+ imageData
6305
+ } = volume;
6306
+ const scalarData = volume.getScalarData();
6307
+ const [dimX, dimY, numFrames] = dimensions;
6308
+ const frameLength = dimX * dimY;
6309
+ const segmentIndices = segmentIndex ? [segmentIndex] : segmentation.segments.filter(segment => segment?.segmentIndex).map(segment => segment.segmentIndex);
6310
+ const segmentIndicesSet = new Set(segmentIndices);
6311
+ const centroids = new Map();
6312
+ for (const index of segmentIndicesSet) {
6313
+ centroids.set(index, {
6314
+ x: 0,
6315
+ y: 0,
6316
+ z: 0,
6317
+ count: 0
6318
+ });
6319
+ }
6320
+ let voxelIndex = 0;
6321
+ for (let frame = 0; frame < numFrames; frame++) {
6322
+ for (let p = 0; p < frameLength; p++) {
6323
+ const segmentIndex = scalarData[voxelIndex++];
6324
+ if (segmentIndicesSet.has(segmentIndex)) {
6325
+ const centroid = centroids.get(segmentIndex);
6326
+ centroid.x += p % dimX;
6327
+ centroid.y += p / dimX | 0;
6328
+ centroid.z += frame;
6329
+ centroid.count++;
6330
+ }
6331
+ }
6332
+ }
6333
+ const result = new Map();
6334
+ for (const [index, centroid] of centroids) {
6335
+ const count = centroid.count;
6336
+ const normalizedCentroid = {
6337
+ x: centroid.x / count,
6338
+ y: centroid.y / count,
6339
+ z: centroid.z / count
6340
+ };
6341
+ normalizedCentroid.world = imageData.indexToWorld([normalizedCentroid.x, normalizedCentroid.y, normalizedCentroid.z]);
6342
+ result.set(index, normalizedCentroid);
6343
+ }
6344
+ this.setCentroids(segmentationId, result);
6345
+ return result;
6346
+ };
6347
+ this.setCentroids = (segmentationId, centroids) => {
6348
+ const segmentation = this.getSegmentation(segmentationId);
6349
+ const imageData = this.getLabelmapVolume(segmentationId).imageData; // Assuming this method returns imageData
6350
+
6351
+ if (!segmentation.cachedStats) {
6352
+ segmentation.cachedStats = {
6353
+ segmentCenter: {}
6354
+ };
6355
+ } else if (!segmentation.cachedStats.segmentCenter) {
6356
+ segmentation.cachedStats.segmentCenter = {};
6357
+ }
6358
+ for (const [segmentIndex, centroid] of centroids) {
6359
+ let world = centroid.world;
6360
+
6361
+ // If world coordinates are not provided, calculate them
6362
+ if (!world || world.length === 0) {
6363
+ world = imageData.indexToWorld(centroid.image);
6364
+ }
6365
+ segmentation.cachedStats.segmentCenter[segmentIndex] = {
6366
+ center: {
6367
+ image: centroid.image,
6368
+ world: world
6369
+ }
6370
+ };
6371
+ }
6372
+ this.addOrUpdateSegmentation(segmentation, true, true);
6373
+ };
6029
6374
  this.createSegmentationForDisplaySet = async (displaySetInstanceUID, options) => {
6030
6375
  const {
6031
6376
  displaySetService
@@ -6055,6 +6400,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6055
6400
  // We should set it as active by default, as it created for display
6056
6401
  isActive: true,
6057
6402
  type: representationType,
6403
+ FrameOfReferenceUID: options?.FrameOfReferenceUID || displaySet.instances?.[0]?.FrameOfReferenceUID,
6058
6404
  representationData: {
6059
6405
  LABELMAP: {
6060
6406
  volumeId: segmentationId,
@@ -6078,6 +6424,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6078
6424
  this.addSegmentationRepresentationToToolGroup = async function (toolGroupId, segmentationId) {
6079
6425
  let hydrateSegmentation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
6080
6426
  let representationType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : dist_esm.Enums.SegmentationRepresentations.Labelmap;
6427
+ let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6081
6428
  const segmentation = _this.getSegmentation(segmentationId);
6082
6429
  if (!segmentation) {
6083
6430
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
@@ -6122,12 +6469,17 @@ class SegmentationService extends src/* PubSubService */.hC {
6122
6469
  if (visibility !== undefined) {
6123
6470
  _this._setSegmentVisibility(segmentationId, segmentIndex, visibility, toolGroupId, suppressEvents);
6124
6471
  }
6125
- if (isLocked !== undefined) {
6472
+ if (isLocked) {
6126
6473
  _this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6127
6474
  }
6128
6475
  }
6476
+ if (!suppressEvents) {
6477
+ _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6478
+ segmentation
6479
+ });
6480
+ }
6129
6481
  };
6130
- this.setSegmentRGBAColorForSegmentation = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6482
+ this.setSegmentRGBAColor = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6131
6483
  const segmentation = this.getSegmentation(segmentationId);
6132
6484
  if (segmentation === undefined) {
6133
6485
  throw new Error(`no segmentation for segmentationId: ${segmentationId}`);
@@ -6153,6 +6505,9 @@ class SegmentationService extends src/* PubSubService */.hC {
6153
6505
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
6154
6506
  }
6155
6507
  segmentation.hydrated = true;
6508
+
6509
+ // Not all segmentations have dipslaysets, some of them are derived in the client
6510
+ _this._setDisplaySetIsHydrated(segmentationId, true);
6156
6511
  if (!suppressEvents) {
6157
6512
  _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6158
6513
  segmentation
@@ -6467,34 +6822,40 @@ class SegmentationService extends src/* PubSubService */.hC {
6467
6822
  this._initSegmentationService();
6468
6823
  }
6469
6824
  /**
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
6825
+ * Adds a new segment to the specified segmentation.
6826
+ * @param segmentationId - The ID of the segmentation to add the segment to.
6827
+ * @param config - An object containing the configuration options for the new segment.
6828
+ * - segmentIndex: (optional) The index of the segment to add. If not provided, the next available index will be used.
6829
+ * - 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.
6830
+ * - properties: (optional) An object containing the properties of the new segment.
6831
+ * - label: (optional) The label of the new segment. If not provided, a default label will be used.
6832
+ * - color: (optional) The color of the new segment in RGB format. If not provided, a default color will be used.
6833
+ * - opacity: (optional) The opacity of the new segment. If not provided, a default opacity will be used.
6834
+ * - visibility: (optional) Whether the new segment should be visible. If not provided, the segment will be visible by default.
6835
+ * - isLocked: (optional) Whether the new segment should be locked for editing. If not provided, the segment will not be locked by default.
6836
+ * - 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
6837
  */
6483
- addSegment(segmentationId, segmentIndex, toolGroupId, properties) {
6484
- if (segmentIndex === 0) {
6838
+ addSegment(segmentationId) {
6839
+ let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6840
+ if (config?.segmentIndex === 0) {
6485
6841
  throw new Error('Segment index 0 is reserved for "no label"');
6486
6842
  }
6487
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
6843
+ const toolGroupId = config.toolGroupId ?? this._getFirstToolGroupId();
6488
6844
  const {
6489
6845
  segmentationRepresentationUID,
6490
6846
  segmentation
6491
6847
  } = this._getSegmentationInfo(segmentationId, toolGroupId);
6848
+ let segmentIndex = config.segmentIndex;
6849
+ if (!segmentIndex) {
6850
+ // grab the next available segment index
6851
+ segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
6852
+ }
6492
6853
  if (this._getSegmentInfo(segmentation, segmentIndex)) {
6493
6854
  throw new Error(`Segment ${segmentIndex} already exists`);
6494
6855
  }
6495
6856
  const rgbaColor = dist_esm.segmentation.config.color.getColorForSegmentIndex(toolGroupId, segmentationRepresentationUID, segmentIndex);
6496
6857
  segmentation.segments[segmentIndex] = {
6497
- label: properties.label,
6858
+ label: config.properties?.label ?? `Segment ${segmentIndex}`,
6498
6859
  segmentIndex: segmentIndex,
6499
6860
  color: [rgbaColor[0], rgbaColor[1], rgbaColor[2]],
6500
6861
  opacity: rgbaColor[3],
@@ -6502,15 +6863,18 @@ class SegmentationService extends src/* PubSubService */.hC {
6502
6863
  isLocked: false
6503
6864
  };
6504
6865
  segmentation.segmentCount++;
6866
+
6867
+ // make the newly added segment the active segment
6868
+ this._setActiveSegment(segmentationId, segmentIndex);
6505
6869
  const suppressEvents = true;
6506
- if (properties !== undefined) {
6870
+ if (config.properties !== undefined) {
6507
6871
  const {
6508
6872
  color: newColor,
6509
6873
  opacity,
6510
6874
  isLocked,
6511
6875
  visibility,
6512
6876
  active
6513
- } = properties;
6877
+ } = config.properties;
6514
6878
  if (newColor !== undefined) {
6515
6879
  this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
6516
6880
  }
@@ -6590,12 +6954,21 @@ class SegmentationService extends src/* PubSubService */.hC {
6590
6954
  let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6591
6955
  this._setSegmentVisibility(segmentationId, segmentIndex, isVisible, toolGroupId, suppressEvents);
6592
6956
  }
6593
- setSegmentLockedForSegmentation(segmentationId, segmentIndex, isLocked) {
6957
+ setSegmentLocked(segmentationId, segmentIndex, isLocked) {
6594
6958
  const suppressEvents = false;
6595
6959
  this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6596
6960
  }
6597
- setSegmentLabel(segmentationId, segmentIndex, segmentLabel) {
6598
- this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
6961
+
6962
+ /**
6963
+ * Toggles the locked state of a segment in a segmentation.
6964
+ * @param segmentationId - The ID of the segmentation.
6965
+ * @param segmentIndex - The index of the segment to toggle.
6966
+ */
6967
+ toggleSegmentLocked(segmentationId, segmentIndex) {
6968
+ const segmentation = this.getSegmentation(segmentationId);
6969
+ const segment = this._getSegmentInfo(segmentation, segmentIndex);
6970
+ const isLocked = !segment.isLocked;
6971
+ this._setSegmentLocked(segmentationId, segmentIndex, isLocked);
6599
6972
  }
6600
6973
  setSegmentColor(segmentationId, segmentIndex, color, toolGroupId) {
6601
6974
  this._setSegmentColor(segmentationId, segmentIndex, color, toolGroupId);
@@ -6608,7 +6981,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6608
6981
  const suppressEvents = false;
6609
6982
  this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
6610
6983
  }
6611
- setActiveSegmentForSegmentation(segmentationId, segmentIndex) {
6984
+ setActiveSegment(segmentationId, segmentIndex) {
6612
6985
  this._setActiveSegment(segmentationId, segmentIndex, false);
6613
6986
  }
6614
6987
 
@@ -6677,11 +7050,14 @@ class SegmentationService extends src/* PubSubService */.hC {
6677
7050
  }
6678
7051
  }]);
6679
7052
 
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);
7053
+ // if first segmentation, we can use the default colorLUT, otherwise
7054
+ // we need to generate a new one and use a new colorLUT
7055
+ const colorLUTIndex = 0;
7056
+ if (Object.keys(this.segmentations).length !== 0) {
7057
+ const newColorLUT = this.generateNewColorLUT();
7058
+ const colorLUTIndex = this.getNextColorLUTIndex();
7059
+ dist_esm.segmentation.config.color.addColorLUT(newColorLUT, colorLUTIndex);
7060
+ }
6685
7061
  this.segmentations[segmentationId] = {
6686
7062
  ...segmentation,
6687
7063
  label: segmentation.label || '',
@@ -6689,8 +7065,8 @@ class SegmentationService extends src/* PubSubService */.hC {
6689
7065
  activeSegmentIndex: segmentation.activeSegmentIndex ?? null,
6690
7066
  segmentCount: segmentation.segmentCount ?? 0,
6691
7067
  isActive: false,
6692
- colorLUTIndex: newColorLUTIndex,
6693
- isVisible: true
7068
+ isVisible: true,
7069
+ colorLUTIndex
6694
7070
  };
6695
7071
  cachedSegmentation = this.segmentations[segmentationId];
6696
7072
  this._updateCornerstoneSegmentations({
@@ -6715,6 +7091,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6715
7091
  id: segmentationId,
6716
7092
  displaySetInstanceUID: segDisplaySet.displaySetInstanceUID,
6717
7093
  type: representationType,
7094
+ label: segDisplaySet.SeriesDescription,
6718
7095
  representationData: {
6719
7096
  [LABELMAP]: {
6720
7097
  volumeId: segmentationId,
@@ -6731,11 +7108,11 @@ class SegmentationService extends src/* PubSubService */.hC {
6731
7108
  return this.addOrUpdateSegmentation(Object.assign(segmentation, cachedSegmentation), suppressEvents);
6732
7109
  }
6733
7110
  const {
6734
- segments,
7111
+ labelmapBufferArray,
6735
7112
  referencedVolumeId
6736
7113
  } = 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.');
7114
+ if (!labelmapBufferArray || !referencedVolumeId) {
7115
+ throw new Error('No labelmapBufferArray or referencedVolumeId found for the SEG displaySet');
6739
7116
  }
6740
7117
 
6741
7118
  // if the labelmap doesn't exist, we need to create it first from the
@@ -6754,72 +7131,27 @@ class SegmentationService extends src/* PubSubService */.hC {
6754
7131
  sharedArrayBuffer: true
6755
7132
  }
6756
7133
  });
6757
- const [rows, columns] = derivedVolume.dimensions;
6758
7134
  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) => {
7135
+ const segmentsInfo = segDisplaySet.segMetadata.data;
7136
+ derivedVolumeScalarData.set(new Uint8Array(labelmapBufferArray[0]));
7137
+ segmentation.segments = segmentsInfo.map((segmentInfo, segmentIndex) => {
7138
+ if (segmentIndex === 0) {
7139
+ return;
7140
+ }
6775
7141
  const {
6776
- pixelData: segPixelData
7142
+ SegmentedPropertyCategoryCodeSequence,
7143
+ SegmentNumber,
7144
+ SegmentLabel,
7145
+ SegmentAlgorithmType,
7146
+ SegmentAlgorithmName,
7147
+ SegmentedPropertyTypeCodeSequence,
7148
+ rgba
6777
7149
  } = 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);
7150
+ const {
7151
+ x,
7152
+ y,
7153
+ z
7154
+ } = segDisplaySet.centroids.get(segmentIndex);
6823
7155
  const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
6824
7156
  segmentation.cachedStats = {
6825
7157
  ...segmentation.cachedStats,
@@ -6834,51 +7166,24 @@ class SegmentationService extends src/* PubSubService */.hC {
6834
7166
  }
6835
7167
  }
6836
7168
  };
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],
7169
+ return {
7170
+ label: SegmentLabel || `Segment ${SegmentNumber}`,
7171
+ segmentIndex: Number(SegmentNumber),
7172
+ category: SegmentedPropertyCategoryCodeSequence ? SegmentedPropertyCategoryCodeSequence.CodeMeaning : '',
7173
+ type: SegmentedPropertyTypeCodeSequence ? SegmentedPropertyTypeCodeSequence.CodeMeaning : '',
7174
+ algorithmType: SegmentAlgorithmType,
7175
+ algorithmName: SegmentAlgorithmName,
7176
+ color: rgba,
7177
+ opacity: 255,
6873
7178
  isVisible: true,
6874
7179
  isLocked: false
6875
7180
  };
6876
7181
  });
7182
+ segmentation.segmentCount = segmentsInfo.length - 1;
6877
7183
  segDisplaySet.isLoaded = true;
6878
7184
  this._broadcastEvent(SegmentationService_EVENTS.SEGMENTATION_LOADING_COMPLETE, {
6879
7185
  segmentationId,
6880
- segDisplaySet,
6881
- overlappingSegments
7186
+ segDisplaySet
6882
7187
  });
6883
7188
  return this.addOrUpdateSegmentation(segmentation, suppressEvents);
6884
7189
  }
@@ -6911,6 +7216,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6911
7216
  id: segmentationId,
6912
7217
  displaySetInstanceUID: rtDisplaySetUID,
6913
7218
  type: representationType,
7219
+ label: rtDisplaySet.SeriesDescription,
6914
7220
  representationData: {
6915
7221
  [CONTOUR]: {
6916
7222
  geometryIds
@@ -7015,6 +7321,9 @@ class SegmentationService extends src/* PubSubService */.hC {
7015
7321
  toolGroupService
7016
7322
  } = this.servicesManager.services;
7017
7323
  const center = this._getSegmentCenter(segmentationId, segmentIndex);
7324
+ if (!center?.world) {
7325
+ return;
7326
+ }
7018
7327
  const {
7019
7328
  world
7020
7329
  } = center;
@@ -7068,6 +7377,17 @@ class SegmentationService extends src/* PubSubService */.hC {
7068
7377
  const adjustedAlpha = type === LABELMAP ? alpha : 1 - alpha;
7069
7378
  highlightFn(segmentIndex, adjustedAlpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation);
7070
7379
  }
7380
+ _setDisplaySetIsHydrated(displaySetUID, isHydrated) {
7381
+ const {
7382
+ displaySetService
7383
+ } = this.servicesManager.services;
7384
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
7385
+ if (!displaySet) {
7386
+ return;
7387
+ }
7388
+ displaySet.isHydrated = isHydrated;
7389
+ displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
7390
+ }
7071
7391
  _highlightLabelmap(segmentIndex, alpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation) {
7072
7392
  const newSegmentSpecificConfig = {
7073
7393
  [segmentIndex]: {
@@ -7170,21 +7490,23 @@ class SegmentationService extends src/* PubSubService */.hC {
7170
7490
 
7171
7491
  if (wasActive) {
7172
7492
  const remainingSegmentations = this._getSegmentations();
7173
- if (remainingSegmentations.length) {
7493
+ const remainingHydratedSegmentations = remainingSegmentations.filter(segmentation => segmentation.hydrated);
7494
+ if (remainingHydratedSegmentations.length) {
7174
7495
  const {
7175
7496
  id
7176
- } = remainingSegmentations[0];
7497
+ } = remainingHydratedSegmentations[0];
7177
7498
  this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
7178
7499
  }
7179
7500
  }
7501
+ this._setDisplaySetIsHydrated(segmentationId, false);
7180
7502
  this._broadcastEvent(this.EVENTS.SEGMENTATION_REMOVED, {
7181
7503
  segmentationId
7182
7504
  });
7183
7505
  }
7184
- setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7185
- this._setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
7506
+ setSegmentLabel(segmentationId, segmentIndex, label) {
7507
+ this._setSegmentLabel(segmentationId, segmentIndex, label);
7186
7508
  }
7187
- _setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7509
+ _setSegmentLabel(segmentationId, segmentIndex, label) {
7188
7510
  let suppressEvents = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
7189
7511
  const segmentation = this.getSegmentation(segmentationId);
7190
7512
  if (segmentation === undefined) {
@@ -7202,16 +7524,14 @@ class SegmentationService extends src/* PubSubService */.hC {
7202
7524
  });
7203
7525
  }
7204
7526
  }
7205
- shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
7206
- if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
7527
+ shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segmentationFrameOfReferenceUID) {
7528
+ if (!viewportDisplaySetInstanceUIDs?.length) {
7207
7529
  return false;
7208
7530
  }
7209
7531
  const {
7210
7532
  displaySetService
7211
7533
  } = this.servicesManager.services;
7212
7534
  let shouldDisplaySeg = false;
7213
- const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
7214
- const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
7215
7535
 
7216
7536
  // check if the displaySet is sharing the same frameOfReferenceUID
7217
7537
  // with the new segmentation
@@ -7220,7 +7540,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7220
7540
 
7221
7541
  // Todo: this might not be ideal for use cases such as 4D, since we
7222
7542
  // don't want to show the segmentation for all the frames
7223
- if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segFrameOfReferenceUID) {
7543
+ if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segmentationFrameOfReferenceUID) {
7224
7544
  shouldDisplaySeg = true;
7225
7545
  break;
7226
7546
  }
@@ -7374,7 +7694,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7374
7694
  }
7375
7695
  _getSegmentationRepresentation(segmentationId, toolGroupId) {
7376
7696
  const segmentationRepresentations = this.getSegmentationRepresentationsForToolGroup(toolGroupId);
7377
- if (segmentationRepresentations.length === 0) {
7697
+ if (!segmentationRepresentations?.length) {
7378
7698
  return;
7379
7699
  }
7380
7700
 
@@ -7659,7 +7979,8 @@ class CornerstoneCacheService {
7659
7979
  }
7660
7980
  _shouldRenderSegmentation(displaySets) {
7661
7981
  const {
7662
- segmentationService
7982
+ segmentationService,
7983
+ displaySetService
7663
7984
  } = this.servicesManager.services;
7664
7985
  const viewportDisplaySetInstanceUIDs = displaySets.map(_ref2 => {
7665
7986
  let {
@@ -7673,7 +7994,8 @@ class CornerstoneCacheService {
7673
7994
  const segmentations = segmentationService.getSegmentations();
7674
7995
  for (const segmentation of segmentations) {
7675
7996
  const segDisplaySetInstanceUID = segmentation.displaySetInstanceUID;
7676
- const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID);
7997
+ const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
7998
+ const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySet.instances[0].FrameOfReferenceUID);
7677
7999
  if (shouldDisplaySeg) {
7678
8000
  return true;
7679
8001
  }
@@ -7747,16 +8069,17 @@ const DEFAULT_TOOLGROUP_ID = 'default';
7747
8069
  // Return true if the data contains the given display set UID OR the imageId
7748
8070
  // if it is a composite object.
7749
8071
  const dataContains = (data, displaySetUID, imageId) => {
7750
- if (data.displaySetInstanceUID === displaySetUID) return true;
8072
+ if (data.displaySetInstanceUID === displaySetUID) {
8073
+ return true;
8074
+ }
7751
8075
  if (imageId && data.isCompositeStack && data.imageIds) {
7752
8076
  return !!data.imageIds.find(dataId => dataId === imageId);
7753
8077
  }
7754
8078
  return false;
7755
8079
  };
7756
8080
  class ViewportInfo {
7757
- constructor(viewportIndex, viewportId) {
8081
+ constructor(viewportId) {
7758
8082
  this.viewportId = '';
7759
- this.viewportIndex = void 0;
7760
8083
  this.element = void 0;
7761
8084
  this.viewportOptions = void 0;
7762
8085
  this.displaySetOptions = void 0;
@@ -7768,7 +8091,6 @@ class ViewportInfo {
7768
8091
  this.viewportOptions = null;
7769
8092
  this.displaySetOptions = null;
7770
8093
  };
7771
- this.viewportIndex = viewportIndex;
7772
8094
  this.viewportId = viewportId;
7773
8095
  this.setPublicViewportOptions({});
7774
8096
  this.setPublicDisplaySetOptions([{}]);
@@ -7779,7 +8101,9 @@ class ViewportInfo {
7779
8101
  * OR if it is a composite stack and contains the given imageId
7780
8102
  */
7781
8103
  contains(displaySetUID, imageId) {
7782
- if (!this.viewportData?.data) return false;
8104
+ if (!this.viewportData?.data) {
8105
+ return false;
8106
+ }
7783
8107
  if (this.viewportData.data.length) {
7784
8108
  return !!this.viewportData.data.find(data => dataContains(data, displaySetUID, imageId));
7785
8109
  }
@@ -7794,9 +8118,6 @@ class ViewportInfo {
7794
8118
  setViewportId(viewportId) {
7795
8119
  this.viewportId = viewportId;
7796
8120
  }
7797
- setViewportIndex(viewportIndex) {
7798
- this.viewportIndex = viewportIndex;
7799
- }
7800
8121
  setElement(element) {
7801
8122
  this.element = element;
7802
8123
  }
@@ -7806,9 +8127,6 @@ class ViewportInfo {
7806
8127
  getViewportData() {
7807
8128
  return this.viewportData;
7808
8129
  }
7809
- getViewportIndex() {
7810
- return this.viewportIndex;
7811
- }
7812
8130
  getElement() {
7813
8131
  return this.element;
7814
8132
  }
@@ -7819,13 +8137,14 @@ class ViewportInfo {
7819
8137
  // map the displaySetOptions and check if they are undefined then set them to default values
7820
8138
  const displaySetOptions = this.mapDisplaySetOptions(publicDisplaySetOptions);
7821
8139
  this.setDisplaySetOptions(displaySetOptions);
8140
+ return this.displaySetOptions;
7822
8141
  }
7823
8142
  hasDisplaySet(displaySetInstanceUID) {
7824
8143
  // Todo: currently this does not work for non image & referenceImage displaySets.
7825
8144
  // Since SEG and other derived displaySets are loaded in a different way, and not
7826
8145
  // via cornerstoneViewportService
7827
8146
  let viewportData = this.getViewportData();
7828
- if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC) {
8147
+ if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC || viewportData.viewportType === esm.Enums.ViewportType.VOLUME_3D) {
7829
8148
  viewportData = viewportData;
7830
8149
  return viewportData.data.some(_ref => {
7831
8150
  let {
@@ -7865,6 +8184,7 @@ class ViewportInfo {
7865
8184
  toolGroupId,
7866
8185
  presentationIds
7867
8186
  });
8187
+ return this.viewportOptions;
7868
8188
  }
7869
8189
  setViewportOptions(viewportOptions) {
7870
8190
  this.viewportOptions = viewportOptions;
@@ -7959,7 +8279,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7959
8279
  constructor(servicesManager) {
7960
8280
  super(CornerstoneViewportService_EVENTS);
7961
8281
  this.renderingEngine = void 0;
7962
- this.viewportsInfo = new Map();
7963
8282
  this.viewportsById = new Map();
7964
8283
  this.viewportGridResizeObserver = void 0;
7965
8284
  this.viewportsDisplaySets = new Map();
@@ -7975,37 +8294,16 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7975
8294
 
7976
8295
  /**
7977
8296
  * Adds the HTML element to the viewportService
7978
- * @param {*} viewportIndex
8297
+ * @param {*} viewportId
7979
8298
  * @param {*} elementRef
7980
8299
  */
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
- }
8300
+ enableViewport(viewportId, elementRef) {
8301
+ const viewportInfo = new Viewport(viewportId);
7996
8302
  viewportInfo.setElement(elementRef);
7997
- this.viewportsInfo.set(viewportIndex, viewportInfo);
7998
8303
  this.viewportsById.set(viewportId, viewportInfo);
7999
8304
  }
8000
8305
  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;
8306
+ return Array.from(this.viewportsById.keys());
8009
8307
  }
8010
8308
 
8011
8309
  /**
@@ -8059,34 +8357,38 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8059
8357
  * created for every new viewport, this will be called whenever the set of
8060
8358
  * viewports is changed, but NOT when the viewport position changes only.
8061
8359
  *
8062
- * @param viewportIndex
8360
+ * @param viewportId - The viewportId to disable
8063
8361
  */
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);
8362
+ disableElement(viewportId) {
8363
+ this.renderingEngine?.disableElement(viewportId);
8364
+
8365
+ // clean up
8073
8366
  this.viewportsById.delete(viewportId);
8367
+ this.viewportsDisplaySets.delete(viewportId);
8074
8368
  }
8075
8369
  setPresentations(viewport, presentations) {
8076
8370
  const properties = presentations?.lutPresentation?.properties;
8077
- if (properties) viewport.setProperties(properties);
8371
+ if (properties) {
8372
+ viewport.setProperties(properties);
8373
+ }
8078
8374
  const camera = presentations?.positionPresentation?.camera;
8079
- if (camera) viewport.setCamera(camera);
8375
+ if (camera) {
8376
+ viewport.setCamera(camera);
8377
+ }
8080
8378
  }
8081
- getPresentation(viewportIndex) {
8082
- const viewportInfo = this.viewportsInfo.get(viewportIndex);
8083
- if (!viewportInfo) return;
8379
+ getPresentation(viewportId) {
8380
+ const viewportInfo = this.viewportsById.get(viewportId);
8381
+ if (!viewportInfo) {
8382
+ return;
8383
+ }
8084
8384
  const {
8085
8385
  viewportType,
8086
8386
  presentationIds
8087
8387
  } = viewportInfo.getViewportOptions();
8088
- const csViewport = this.getCornerstoneViewportByIndex(viewportIndex);
8089
- if (!csViewport) return;
8388
+ const csViewport = this.getCornerstoneViewport(viewportId);
8389
+ if (!csViewport) {
8390
+ return;
8391
+ }
8090
8392
  const properties = csViewport.getProperties();
8091
8393
  if (properties.isComputedVOI) {
8092
8394
  delete properties.voiRange;
@@ -8102,40 +8404,78 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8102
8404
  camera
8103
8405
  };
8104
8406
  }
8407
+ storePresentation(_ref) {
8408
+ let {
8409
+ viewportId
8410
+ } = _ref;
8411
+ const stateSyncService = this.servicesManager.services.stateSyncService;
8412
+ let presentation;
8413
+ try {
8414
+ presentation = this.getPresentation(viewportId);
8415
+ } catch (error) {
8416
+ console.warn(error);
8417
+ }
8418
+ if (!presentation || !presentation.presentationIds) {
8419
+ return;
8420
+ }
8421
+ const {
8422
+ lutPresentationStore,
8423
+ positionPresentationStore
8424
+ } = stateSyncService.getState();
8425
+ const {
8426
+ presentationIds
8427
+ } = presentation;
8428
+ const {
8429
+ lutPresentationId,
8430
+ positionPresentationId
8431
+ } = presentationIds || {};
8432
+ const storeState = {};
8433
+ if (lutPresentationId) {
8434
+ storeState.lutPresentationStore = {
8435
+ ...lutPresentationStore,
8436
+ [lutPresentationId]: presentation
8437
+ };
8438
+ }
8439
+ if (positionPresentationId) {
8440
+ storeState.positionPresentationStore = {
8441
+ ...positionPresentationStore,
8442
+ [positionPresentationId]: presentation
8443
+ };
8444
+ }
8445
+ stateSyncService.store(storeState);
8446
+ }
8105
8447
 
8106
8448
  /**
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
8449
+ * Sets the viewport data for a viewport.
8450
+ * @param viewportId - The ID of the viewport to set the data for.
8451
+ * @param viewportData - The viewport data to set.
8452
+ * @param publicViewportOptions - The public viewport options.
8453
+ * @param publicDisplaySetOptions - The public display set options.
8454
+ * @param presentations - The presentations to set.
8113
8455
  */
8114
- setViewportData(viewportIndex, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8456
+ setViewportData(viewportId, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8115
8457
  const renderingEngine = this.getRenderingEngine();
8116
- const viewportId = publicViewportOptions.viewportId || this.getViewportId(viewportIndex);
8117
- if (!viewportId) {
8118
- throw new Error('Must define viewportId externally');
8119
- }
8458
+
8459
+ // This is the old viewportInfo, which may have old options but we might be
8460
+ // using its viewport (same viewportId as the new viewportInfo)
8120
8461
  const viewportInfo = this.viewportsById.get(viewportId);
8462
+
8463
+ // We should store the presentation for the current viewport since we can't only
8464
+ // rely to store it WHEN the viewport is disabled since we might keep around the
8465
+ // same viewport/element and just change the viewportData for it (drag and drop etc.)
8466
+ // the disableElement storePresentation handle would not be called in this case
8467
+ // and we would lose the presentation.
8468
+ this.storePresentation({
8469
+ viewportId: viewportInfo.getViewportId()
8470
+ });
8121
8471
  if (!viewportInfo) {
8122
- throw new Error('Viewport info not defined');
8472
+ throw new Error('element is not enabled for the given viewportId');
8123
8473
  }
8124
8474
 
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);
8475
+ // override the viewportOptions and displaySetOptions with the public ones
8476
+ // since those are the newly set ones, we set them here so that it handles defaults
8477
+ const displaySetOptions = viewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
8478
+ const viewportOptions = viewportInfo.setPublicViewportOptions(publicViewportOptions);
8139
8479
  const element = viewportInfo.getElement();
8140
8480
  const type = viewportInfo.getViewportType();
8141
8481
  const background = viewportInfo.getBackground();
@@ -8150,11 +8490,23 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8150
8490
  }
8151
8491
  };
8152
8492
 
8493
+ // Rendering Engine Id set should happen before enabling the element
8494
+ // since there are callbacks that depend on the renderingEngine id
8495
+ // Todo: however, this is a limitation which means that we can't change
8496
+ // the rendering engine id for a given viewport which might be a super edge
8497
+ // case
8498
+ viewportInfo.setRenderingEngineId(renderingEngine.id);
8499
+
8153
8500
  // Todo: this is not optimal at all, we are re-enabling the already enabled
8154
8501
  // element which is not what we want. But enabledElement as part of the
8155
8502
  // renderingEngine is designed to be used like this. This will trigger
8156
8503
  // ENABLED_ELEMENT again and again, which will run onEnableElement callbacks
8157
8504
  renderingEngine.enableElement(viewportInput);
8505
+ viewportInfo.setViewportOptions(viewportOptions);
8506
+ viewportInfo.setDisplaySetOptions(displaySetOptions);
8507
+ viewportInfo.setViewportData(viewportData);
8508
+ viewportInfo.setViewportId(viewportId);
8509
+ this.viewportsById.set(viewportId, viewportInfo);
8158
8510
  const viewport = renderingEngine.getViewport(viewportId);
8159
8511
  this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
8160
8512
 
@@ -8163,7 +8515,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8163
8515
  // invalid data.
8164
8516
  this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
8165
8517
  viewportData,
8166
- viewportIndex,
8167
8518
  viewportId
8168
8519
  });
8169
8520
  }
@@ -8175,31 +8526,8 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8175
8526
  const viewport = this.renderingEngine.getViewport(viewportId);
8176
8527
  return viewport;
8177
8528
  }
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
8529
  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;
8530
+ return this.viewportsById.get(viewportId);
8203
8531
  }
8204
8532
  _setStackViewport(viewport, viewportData, viewportInfo, presentations) {
8205
8533
  const displaySetOptions = viewportInfo.getDisplaySetOptions();
@@ -8236,9 +8564,13 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8236
8564
  }
8237
8565
  }
8238
8566
  viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
8239
- viewport.setProperties(properties);
8567
+ viewport.setProperties({
8568
+ ...properties
8569
+ });
8240
8570
  const camera = presentations.positionPresentation?.camera;
8241
- if (camera) viewport.setCamera(camera);
8571
+ if (camera) {
8572
+ viewport.setCamera(camera);
8573
+ }
8242
8574
  });
8243
8575
  }
8244
8576
  _getInitialImageIndexForViewport(viewportInfo, imageIds) {
@@ -8333,7 +8665,7 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8333
8665
  });
8334
8666
  }
8335
8667
  this.viewportsDisplaySets.set(viewport.id, displaySetInstanceUIDs);
8336
- if (hangingProtocolService.hasCustomImageLoadStrategy() && !hangingProtocolService.customImageLoadPerformed) {
8668
+ if (hangingProtocolService.getShouldPerformCustomImageLoad()) {
8337
8669
  // delegate the volume loading to the hanging protocol service if it has a custom image load strategy
8338
8670
  return hangingProtocolService.runImageLoadStrategy({
8339
8671
  viewportId: viewport.id,
@@ -8395,11 +8727,11 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8395
8727
  };
8396
8728
  });
8397
8729
  await viewport.setVolumes(volumeInputArray);
8398
- volumesProperties.forEach(_ref => {
8730
+ volumesProperties.forEach(_ref2 => {
8399
8731
  let {
8400
8732
  properties,
8401
8733
  volumeId
8402
- } = _ref;
8734
+ } = _ref2;
8403
8735
  viewport.setProperties(properties, volumeId);
8404
8736
  });
8405
8737
  this.setPresentations(viewport, presentations);
@@ -8448,13 +8780,25 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8448
8780
  continue;
8449
8781
  }
8450
8782
 
8451
- // otherwise, check if the hydrated segmentations are in the same FOR
8783
+ // otherwise, check if the hydrated segmentations are in the same FrameOfReferenceUID
8452
8784
  // as the primary displaySet, if so add the representation (since it was not there)
8453
8785
  const {
8454
- id: segDisplaySetInstanceUID,
8455
- type
8786
+ id: segDisplaySetInstanceUID
8456
8787
  } = segmentation;
8457
- const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8788
+ let segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8789
+ if (!segFrameOfReferenceUID) {
8790
+ // if the segmentation displaySet does not have a FrameOfReferenceUID, we might check the
8791
+ // segmentation itself maybe it has a FrameOfReferenceUID
8792
+ const {
8793
+ FrameOfReferenceUID
8794
+ } = segmentation;
8795
+ if (FrameOfReferenceUID) {
8796
+ segFrameOfReferenceUID = FrameOfReferenceUID;
8797
+ }
8798
+ }
8799
+ if (!segFrameOfReferenceUID) {
8800
+ return;
8801
+ }
8458
8802
  let shouldDisplaySeg = false;
8459
8803
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
8460
8804
  const primaryFrameOfReferenceUID = this._getFrameOfReferenceUID(displaySetInstanceUID);
@@ -8487,13 +8831,12 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8487
8831
 
8488
8832
  // Todo: keepCamera is an interim solution until we have a better solution for
8489
8833
  // keeping the camera position when the viewport data is changed
8490
- updateViewport(viewportIndex, viewportData) {
8834
+ updateViewport(viewportId, viewportData) {
8491
8835
  let keepCamera = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8492
- const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
8493
- const viewportId = viewportInfo.getViewportId();
8836
+ const viewportInfo = this.getViewportInfo(viewportId);
8494
8837
  const viewport = this.getCornerstoneViewport(viewportId);
8495
8838
  const viewportCamera = viewport.getCamera();
8496
- if (viewport instanceof esm.VolumeViewport) {
8839
+ if (viewport instanceof esm.VolumeViewport || viewport instanceof esm.VolumeViewport3D) {
8497
8840
  this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
8498
8841
  if (keepCamera) {
8499
8842
  viewport.setCamera(viewportCamera);
@@ -8548,23 +8891,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8548
8891
  return slabThickness;
8549
8892
  }
8550
8893
  }
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
8894
  _getFrameOfReferenceUID(displaySetInstanceUID) {
8569
8895
  const {
8570
8896
  displaySetService
@@ -8602,28 +8928,28 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8602
8928
  *
8603
8929
  * @param measurement
8604
8930
  * The measurement that is desired to view.
8605
- * @param activeViewportIndex - the index that was active at the time the jump
8931
+ * @param activeViewportId - the index that was active at the time the jump
8606
8932
  * was initiated.
8607
- * @return the viewportIndex to display the given measurement
8933
+ * @return the viewportId that the measurement should be displayed in.
8608
8934
  */
8609
- getViewportIndexToJump(activeViewportIndex, displaySetInstanceUID, cameraProps) {
8610
- const viewportInfo = this.viewportsInfo.get(activeViewportIndex);
8935
+ getViewportIdToJump(activeViewportId, displaySetInstanceUID, cameraProps) {
8936
+ const viewportInfo = this.getViewportInfo(activeViewportId);
8611
8937
  const {
8612
8938
  referencedImageId
8613
8939
  } = cameraProps;
8614
8940
  if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
8615
- return activeViewportIndex;
8941
+ return activeViewportId;
8616
8942
  }
8617
- return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportIndex ?? -1;
8943
+ return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportId ?? null;
8618
8944
  }
8619
8945
  }
8620
8946
  CornerstoneViewportService.REGISTRATION = {
8621
8947
  name: 'cornerstoneViewportService',
8622
8948
  altName: 'CornerstoneViewportService',
8623
- create: _ref2 => {
8949
+ create: _ref3 => {
8624
8950
  let {
8625
8951
  servicesManager
8626
- } = _ref2;
8952
+ } = _ref3;
8627
8953
  return new CornerstoneViewportService(servicesManager);
8628
8954
  }
8629
8955
  };
@@ -8632,7 +8958,7 @@ CornerstoneViewportService.REGISTRATION = {
8632
8958
 
8633
8959
 
8634
8960
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
8635
- var dicomweb_client_es = __webpack_require__(75935);
8961
+ var dicomweb_client_es = __webpack_require__(97604);
8636
8962
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/dicomLoaderService.js
8637
8963
 
8638
8964
 
@@ -8707,7 +9033,7 @@ class DicomLoaderService {
8707
9033
  // Use referenced imageInstance
8708
9034
  const imageInstance = getImageInstance(dataset);
8709
9035
  const nonImageInstance = getNonImageInstance(dataset);
8710
- if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId.startsWith('dicomfile')) {
9036
+ if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId?.startsWith('dicomfile')) {
8711
9037
  return;
8712
9038
  }
8713
9039
  const instance = imageInstance || nonImageInstance;
@@ -8824,7 +9150,6 @@ function getHandlesFromPoints(points) {
8824
9150
 
8825
9151
 
8826
9152
 
8827
-
8828
9153
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/index.ts
8829
9154
 
8830
9155
 
@@ -8849,11 +9174,10 @@ function src_extends() { src_extends = Object.assign ? Object.assign.bind() : fu
8849
9174
 
8850
9175
 
8851
9176
 
8852
-
8853
9177
 
8854
9178
 
8855
9179
  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));
9180
+ return Promise.all(/* import() */[__webpack_require__.e(23), __webpack_require__.e(181)]).then(__webpack_require__.bind(__webpack_require__, 86181));
8857
9181
  });
8858
9182
  const OHIFCornerstoneViewport = props => {
8859
9183
  return /*#__PURE__*/react.createElement(react.Suspense, {
@@ -8876,7 +9200,6 @@ const cornerstoneExtension = {
8876
9200
  esm.imageLoadPoolManager.clearRequestStack(type);
8877
9201
  esm.imageRetrievalPoolManager.clearRequestStack(type);
8878
9202
  });
8879
- destroy();
8880
9203
  (0,state/* reset */.mc)();
8881
9204
  },
8882
9205
  /**
@@ -8956,7 +9279,7 @@ const cornerstoneExtension = {
8956
9279
 
8957
9280
  /***/ }),
8958
9281
 
8959
- /***/ 21922:
9282
+ /***/ 73704:
8960
9283
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8961
9284
 
8962
9285
  "use strict";
@@ -8976,21 +9299,21 @@ const state = {
8976
9299
  * @param {HTMLElement} dom Active viewport element.
8977
9300
  * @return void
8978
9301
  */
8979
- const setEnabledElement = (viewportIndex, element, context) => {
9302
+ const setEnabledElement = (viewportId, element, context) => {
8980
9303
  const targetContext = context || state.DEFAULT_CONTEXT;
8981
- state.enabledElements[viewportIndex] = {
9304
+ state.enabledElements[viewportId] = {
8982
9305
  element,
8983
9306
  context: targetContext
8984
9307
  };
8985
9308
  };
8986
9309
 
8987
9310
  /**
8988
- * Grabs the enabled element `dom` reference of an ative viewport.
9311
+ * Grabs the enabled element `dom` reference of an active viewport.
8989
9312
  *
8990
9313
  * @return {HTMLElement} Active viewport element.
8991
9314
  */
8992
- const getEnabledElement = viewportIndex => {
8993
- return state.enabledElements[viewportIndex];
9315
+ const getEnabledElement = viewportId => {
9316
+ return state.enabledElements[viewportId];
8994
9317
  };
8995
9318
  const reset = () => {
8996
9319
  state.enabledElements = {};
@@ -8999,14 +9322,14 @@ const reset = () => {
8999
9322
 
9000
9323
  /***/ }),
9001
9324
 
9002
- /***/ 63130:
9325
+ /***/ 87172:
9003
9326
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9004
9327
 
9005
9328
  "use strict";
9006
9329
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9007
9330
  /* harmony export */ Z: () => (/* binding */ getSOPInstanceAttributes)
9008
9331
  /* harmony export */ });
9009
- /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77331);
9332
+ /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(45754);
9010
9333
 
9011
9334
 
9012
9335
  /**