@ohif/app 3.7.0-beta.9 → 3.7.0-beta.91

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.0edb40e9d9467dd3a189.js → 12.bundle.e8d2ae45df74384fd980.js} +6 -6
  2. package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.253395f320b72180da63.js} +6 -6
  3. package/dist/{351.bundle.0742237651aef9694a65.js → 181.bundle.73fc96c6b3ab1fabedc8.js} +226 -204
  4. package/dist/{351.css → 181.css} +1 -1
  5. package/dist/{744.bundle.c459c690581bc8a522d8.js → 19.bundle.839e1ef2a3468da3ae6e.js} +240 -375
  6. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.96bbb4547a346fe3921f.js} +1420 -750
  7. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  8. package/dist/221.bundle.39ad5abb775af5702ddb.js +1723 -0
  9. package/dist/221.css +2 -0
  10. package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
  11. package/dist/{976.bundle.3f8bfb620791f4508420.js → 236.bundle.a24efa76d228c38e56d0.js} +88 -104
  12. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.4bebed43526c7e06344f.js} +52 -36
  13. package/dist/{973.bundle.5aa91607481865ead93f.js → 281.bundle.b50f5766fb3f79b2ed9d.js} +18 -14
  14. package/dist/{82.bundle.978be6f7595202cd342b.js → 342.bundle.40cf206d7cddf20933eb.js} +1765 -476
  15. package/dist/{404.bundle.83acdec604ed84f4b772.js → 359.bundle.b13a62377d68750cbf34.js} +46 -131
  16. package/dist/{192.bundle.655fc9c5aeff41110aa9.js → 370.bundle.f25928b95a42ac10c7a7.js} +113 -99
  17. package/dist/{790.bundle.08e37fd3b64af8dd8e78.js → 410.bundle.cc5008512de45d69e1cb.js} +11 -9
  18. package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.af0a207c29b109f84159.js} +49 -17
  19. package/dist/{569.bundle.c8e771a8d28e237b32be.js → 451.bundle.1c714bfb8b66d3a5adfb.js} +86 -106
  20. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.4aaec34d87b0c93687c1.js} +78 -99
  21. package/dist/{199.bundle.251f86c6e2eee85c49a5.js → 506.bundle.817e9b84a97639aa147d.js} +11 -9
  22. package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 530.bundle.a03b6f942ace3e1baa1e.js} +726 -447
  23. package/dist/579.css +1 -0
  24. package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
  25. package/dist/613.bundle.37227666b898dfdfe9c1.js +532 -0
  26. package/dist/{984.bundle.0c8b7d8388a662ad5ebc.js → 663.bundle.73495b93aa8cb540241c.js} +68 -38
  27. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.dccef1f36e4bc79bcc48.js} +6 -6
  28. package/dist/{50.bundle.bec52570fe00c2ccced8.js → 687.bundle.3d415b0f43c3e19ef6ef.js} +218 -9
  29. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 743.bundle.4bfe6e562ffb2c22708f.js} +26281 -21326
  30. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.7528cba56a1407357144.js} +95 -64
  31. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  32. package/dist/{283.bundle.b43e001c27e02b0199aa.js → 782.bundle.4bf97e067cf8067f2239.js} +117 -67
  33. package/dist/{642.bundle.1ab1e9ea67caeaedb189.js → 814.bundle.9d5edb8094787e02f22f.js} +6 -6
  34. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.00de6455c18be0307b41.js} +81 -34
  35. package/dist/831.bundle.83658f62fcc769043605.js +16700 -0
  36. package/dist/{707.bundle.9622c314b0ea3488d69a.js → 877.bundle.b83c182be6ee497d2df7.js} +1022 -708
  37. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.7324d84913daffb6a4c4.js} +34 -29
  38. package/dist/945.min.worker.js +1 -1
  39. package/dist/945.min.worker.js.map +1 -1
  40. package/dist/{270.bundle.4564621556b0f963a004.js → 957.bundle.8c09a01840ab8aa32734.js} +7093 -987
  41. package/dist/{208.bundle.05451122c341d80d3c22.js → 99.bundle.e6844ea7f331fd2ae35a.js} +85 -104
  42. package/dist/_redirects +1 -1
  43. package/dist/app-config.js +35 -17
  44. package/dist/{app.bundle.d2ebd2fcc8b88864ebeb.js → app.bundle.6c7d9f72689fc7c05040.js} +71939 -66734
  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 -22
  61. package/dist/616.bundle.d0581701281977bea39b.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 → 877.css} +0 -0
@@ -1,6 +1,6 @@
1
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[707],{
1
+ (self["webpackChunk"] = self["webpackChunk"] || []).push([[877],{
2
2
 
3
- /***/ 31197:
3
+ /***/ 94848:
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 + 331 modules
37
+ var esm = __webpack_require__(3743);
38
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 348 modules
39
+ var dist_esm = __webpack_require__(14957);
40
+ // EXTERNAL MODULE: ../../core/src/index.ts + 64 modules
41
+ var src = __webpack_require__(67869);
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,223 @@ 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 instanceof Array) {
489
+ pixelData = overlay.pixelData[0];
490
+ } else if (overlay.pixelData.retrieveBulkData) {
491
+ pixelData = await overlay.pixelData.retrieveBulkData();
492
+ }
493
+ if (!pixelData) {
494
+ return;
495
+ }
496
+ const dataUrl = this._renderOverlayToDataUrl({
497
+ width: overlay.columns,
498
+ height: overlay.rows
499
+ }, color, pixelData);
500
+ return {
501
+ ...overlay,
502
+ _id: (0,utils/* guid */.M8)(),
503
+ dataUrl,
504
+ // this will be a data url expression of the rendered image
505
+ color
506
+ };
507
+ }));
508
+ this._cachedStats[imageId] = {
509
+ color: color,
510
+ overlays: overlays.filter(overlay => overlay)
511
+ };
512
+ return this._cachedStats[imageId];
513
+ }
514
+
515
+ /**
516
+ * compare two RGBA expression of colors.
517
+ *
518
+ * @param color1
519
+ * @param color2
520
+ * @returns
521
+ */
522
+ _isSameColor(color1, color2) {
523
+ return color1 && color2 && color1[0] === color2[0] && color1[1] === color2[1] && color1[2] === color2[2] && color1[3] === color2[3];
524
+ }
525
+
526
+ /**
527
+ * pixelData of overlayPlane module is an array of bits corresponding
528
+ * to each of the underlying pixels of the image.
529
+ * Let's create pixel data from bit array of overlay data
530
+ *
531
+ * @param pixelDataRaw
532
+ * @param color
533
+ * @returns
534
+ */
535
+ _renderOverlayToDataUrl(_ref, color, pixelDataRaw) {
536
+ let {
537
+ width,
538
+ height
539
+ } = _ref;
540
+ const pixelDataView = new DataView(pixelDataRaw);
541
+ const totalBits = width * height;
542
+ const canvas = document.createElement('canvas');
543
+ canvas.width = width;
544
+ canvas.height = height;
545
+ const ctx = canvas.getContext('2d');
546
+ ctx.clearRect(0, 0, width, height); // make it transparent
547
+ ctx.globalCompositeOperation = 'copy';
548
+ const imageData = ctx.getImageData(0, 0, width, height);
549
+ const data = imageData.data;
550
+ for (let i = 0, bitIdx = 0, byteIdx = 0; i < totalBits; i++) {
551
+ if (pixelDataView.getUint8(byteIdx) & 1 << bitIdx) {
552
+ data[i * 4] = color[0];
553
+ data[i * 4 + 1] = color[1];
554
+ data[i * 4 + 2] = color[2];
555
+ data[i * 4 + 3] = color[3];
556
+ }
557
+
558
+ // next bit, byte
559
+ if (bitIdx >= 7) {
560
+ bitIdx = 0;
561
+ byteIdx++;
562
+ } else {
563
+ bitIdx++;
564
+ }
565
+ }
566
+ ctx.putImageData(imageData, 0, 0);
567
+ return canvas.toDataURL();
568
+ }
569
+ }
570
+ ImageOverlayViewerTool.toolName = 'ImageOverlayViewer';
571
+ /* harmony default export */ const tools_ImageOverlayViewerTool = (ImageOverlayViewerTool);
361
572
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/initCornerstoneTools.js
362
573
 
363
574
 
575
+
364
576
  function initCornerstoneTools() {
365
577
  let configuration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
366
578
  dist_esm.CrosshairsTool.isAnnotation = false;
@@ -390,6 +602,10 @@ function initCornerstoneTools() {
390
602
  (0,dist_esm.addTool)(dist_esm.ReferenceLinesTool);
391
603
  (0,dist_esm.addTool)(tools_CalibrationLineTool);
392
604
  (0,dist_esm.addTool)(dist_esm.TrackballRotateTool);
605
+ (0,dist_esm.addTool)(dist_esm.CircleScissorsTool);
606
+ (0,dist_esm.addTool)(dist_esm.RectangleScissorsTool);
607
+ (0,dist_esm.addTool)(dist_esm.SphereScissorsTool);
608
+ (0,dist_esm.addTool)(tools_ImageOverlayViewerTool);
393
609
 
394
610
  // Modify annotation tools to use dashed lines on SR
395
611
  const annotationStyle = {
@@ -428,13 +644,17 @@ const toolNames = {
428
644
  SegmentationDisplay: dist_esm.SegmentationDisplayTool.toolName,
429
645
  ReferenceLines: dist_esm.ReferenceLinesTool.toolName,
430
646
  CalibrationLine: tools_CalibrationLineTool.toolName,
431
- TrackballRotateTool: dist_esm.TrackballRotateTool.toolName
647
+ TrackballRotateTool: dist_esm.TrackballRotateTool.toolName,
648
+ CircleScissors: dist_esm.CircleScissorsTool.toolName,
649
+ RectangleScissors: dist_esm.RectangleScissorsTool.toolName,
650
+ SphereScissors: dist_esm.SphereScissorsTool.toolName,
651
+ ImageOverlayViewer: tools_ImageOverlayViewerTool.toolName
432
652
  };
433
653
 
434
654
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js
435
655
  /* harmony default export */ const supportedTools = (['Length', 'EllipticalROI', 'CircleROI', 'Bidirectional', 'ArrowAnnotate', 'Angle', 'CobbAngle', 'Probe', 'RectangleROI', 'PlanarFreehandROI']);
436
656
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js
437
- var getSOPInstanceAttributes = __webpack_require__(63130);
657
+ var getSOPInstanceAttributes = __webpack_require__(87172);
438
658
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts
439
659
 
440
660
 
@@ -537,9 +757,9 @@ function getMappedAnnotations(annotation, displaySetService) {
537
757
  SeriesNumber
538
758
  } = displaySet;
539
759
  const {
540
- length
760
+ length,
761
+ unit = 'mm'
541
762
  } = targetStats;
542
- const unit = 'mm';
543
763
  annotations.push({
544
764
  SeriesInstanceUID,
545
765
  SOPInstanceUID,
@@ -566,10 +786,13 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
566
786
  values.push('Cornerstone:Length');
567
787
  mappedAnnotations.forEach(annotation => {
568
788
  const {
569
- length
789
+ length,
790
+ unit
570
791
  } = annotation;
571
- columns.push(`Length (mm)`);
792
+ columns.push(`Length`);
572
793
  values.push(length);
794
+ columns.push('Unit');
795
+ values.push(unit);
573
796
  });
574
797
  if (FrameOfReferenceUID) {
575
798
  columns.push('FrameOfReferenceUID');
@@ -598,7 +821,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
598
821
  length,
599
822
  SeriesNumber,
600
823
  SOPInstanceUID,
601
- frameNumber
824
+ frameNumber,
825
+ unit
602
826
  } = mappedAnnotations[0];
603
827
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
604
828
  let InstanceNumber;
@@ -607,9 +831,11 @@ function getDisplayText(mappedAnnotations, displaySet) {
607
831
  }
608
832
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
609
833
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
610
- if (length === null || length === undefined) return displayText;
834
+ if (length === null || length === undefined) {
835
+ return displayText;
836
+ }
611
837
  const roundedLength = src.utils.roundNumber(length, 2);
612
- displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
838
+ displayText.push(`${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
613
839
  return displayText;
614
840
  }
615
841
  /* harmony default export */ const measurementServiceMappings_Length = (Length);
@@ -711,9 +937,9 @@ function Bidirectional_getMappedAnnotations(annotation, displaySetService) {
711
937
  } = displaySet;
712
938
  const {
713
939
  length,
714
- width
940
+ width,
941
+ unit
715
942
  } = targetStats;
716
- const unit = 'mm';
717
943
  annotations.push({
718
944
  SeriesInstanceUID,
719
945
  SOPInstanceUID,
@@ -742,10 +968,11 @@ function Bidirectional_getReport(mappedAnnotations, points, FrameOfReferenceUID)
742
968
  mappedAnnotations.forEach(annotation => {
743
969
  const {
744
970
  length,
745
- width
971
+ width,
972
+ unit
746
973
  } = annotation;
747
- columns.push(`Length (mm)`, `Width (mm)`);
748
- values.push(length, width);
974
+ columns.push(`Length`, `Width`, 'Unit');
975
+ values.push(length, width, unit);
749
976
  });
750
977
  if (FrameOfReferenceUID) {
751
978
  columns.push('FrameOfReferenceUID');
@@ -773,6 +1000,7 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
773
1000
  const {
774
1001
  length,
775
1002
  width,
1003
+ unit,
776
1004
  SeriesNumber,
777
1005
  SOPInstanceUID,
778
1006
  frameNumber
@@ -786,27 +1014,15 @@ function Bidirectional_getDisplayText(mappedAnnotations, displaySet) {
786
1014
  }
787
1015
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
788
1016
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
789
- displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
790
- displayText.push(`W: ${roundedWidth} mm`);
1017
+ displayText.push(`L: ${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1018
+ displayText.push(`W: ${roundedWidth} ${unit}`);
791
1019
  return displayText;
792
1020
  }
793
1021
  /* 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
1022
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts
806
1023
 
807
1024
 
808
1025
 
809
-
810
1026
  const EllipticalROI = {
811
1027
  toAnnotation: measurement => {},
812
1028
  toMeasurement: (csToolsEventDetail, displaySetService, cornerstoneViewportService, getValueTypeFromToolType) => {
@@ -904,16 +1120,18 @@ function EllipticalROI_getMappedAnnotations(annotation, displaySetService) {
904
1120
  stdDev,
905
1121
  max,
906
1122
  area,
907
- Modality
1123
+ Modality,
1124
+ areaUnit,
1125
+ modalityUnit
908
1126
  } = targetStats;
909
- const unit = utils_getModalityUnit(Modality);
910
1127
  annotations.push({
911
1128
  SeriesInstanceUID,
912
1129
  SOPInstanceUID,
913
1130
  SeriesNumber,
914
1131
  frameNumber,
915
1132
  Modality,
916
- unit,
1133
+ unit: modalityUnit,
1134
+ areaUnit,
917
1135
  mean,
918
1136
  stdDev,
919
1137
  max,
@@ -941,13 +1159,14 @@ function EllipticalROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
941
1159
  stdDev,
942
1160
  max,
943
1161
  area,
944
- unit
1162
+ unit,
1163
+ areaUnit
945
1164
  } = annotation;
946
1165
  if (!mean || !unit || !max || !area) {
947
1166
  return;
948
1167
  }
949
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
950
- values.push(max, mean, stdDev, area);
1168
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1169
+ values.push(max, mean, stdDev, area, areaUnit);
951
1170
  });
952
1171
  if (FrameOfReferenceUID) {
953
1172
  columns.push('FrameOfReferenceUID');
@@ -975,7 +1194,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
975
1194
  const {
976
1195
  area,
977
1196
  SOPInstanceUID,
978
- frameNumber
1197
+ frameNumber,
1198
+ areaUnit
979
1199
  } = mappedAnnotations[0];
980
1200
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
981
1201
  let InstanceNumber;
@@ -984,10 +1204,8 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
984
1204
  }
985
1205
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
986
1206
  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>`);
1207
+ const roundedArea = src.utils.roundNumber(area, 2);
1208
+ displayText.push(`${roundedArea} ${areaUnit}`);
991
1209
 
992
1210
  // Todo: we need a better UI for displaying all these information
993
1211
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -1013,7 +1231,6 @@ function EllipticalROI_getDisplayText(mappedAnnotations, displaySet) {
1013
1231
 
1014
1232
 
1015
1233
 
1016
-
1017
1234
  const CircleROI = {
1018
1235
  toAnnotation: measurement => {},
1019
1236
  toMeasurement: (csToolsEventDetail, DisplaySetService, CornerstoneViewportService, getValueTypeFromToolType) => {
@@ -1111,20 +1328,22 @@ function CircleROI_getMappedAnnotations(annotation, DisplaySetService) {
1111
1328
  stdDev,
1112
1329
  max,
1113
1330
  area,
1114
- Modality
1331
+ Modality,
1332
+ areaUnit,
1333
+ modalityUnit
1115
1334
  } = targetStats;
1116
- const unit = utils_getModalityUnit(Modality);
1117
1335
  annotations.push({
1118
1336
  SeriesInstanceUID,
1119
1337
  SOPInstanceUID,
1120
1338
  SeriesNumber,
1121
1339
  frameNumber,
1122
1340
  Modality,
1123
- unit,
1341
+ unit: modalityUnit,
1124
1342
  mean,
1125
1343
  stdDev,
1126
1344
  max,
1127
- area
1345
+ area,
1346
+ areaUnit
1128
1347
  });
1129
1348
  });
1130
1349
  return annotations;
@@ -1148,13 +1367,14 @@ function CircleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID) {
1148
1367
  stdDev,
1149
1368
  max,
1150
1369
  area,
1151
- unit
1370
+ unit,
1371
+ areaUnit
1152
1372
  } = annotation;
1153
1373
  if (!mean || !unit || !max || !area) {
1154
1374
  return;
1155
1375
  }
1156
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1157
- values.push(max, mean, stdDev, area);
1376
+ columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
1377
+ values.push(max, mean, stdDev, area, areaUnit);
1158
1378
  });
1159
1379
  if (FrameOfReferenceUID) {
1160
1380
  columns.push('FrameOfReferenceUID');
@@ -1182,7 +1402,8 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1182
1402
  const {
1183
1403
  area,
1184
1404
  SOPInstanceUID,
1185
- frameNumber
1405
+ frameNumber,
1406
+ areaUnit
1186
1407
  } = mappedAnnotations[0];
1187
1408
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
1188
1409
  let InstanceNumber;
@@ -1194,7 +1415,7 @@ function CircleROI_getDisplayText(mappedAnnotations, displaySet) {
1194
1415
 
1195
1416
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
1196
1417
  const roundedArea = src.utils.roundNumber(area || 0, 2);
1197
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
1418
+ displayText.push(`${roundedArea} ${areaUnit}`);
1198
1419
 
1199
1420
  // Todo: we need a better UI for displaying all these information
1200
1421
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -1515,7 +1736,9 @@ function CobbAngle_getDisplayText(mappedAnnotations, displaySet) {
1515
1736
  }
1516
1737
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1517
1738
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1518
- if (angle === undefined) return displayText;
1739
+ if (angle === undefined) {
1740
+ return displayText;
1741
+ }
1519
1742
  const roundedAngle = src.utils.roundNumber(angle, 2);
1520
1743
  displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1521
1744
  return displayText;
@@ -1695,7 +1918,9 @@ function Angle_getDisplayText(mappedAnnotations, displaySet) {
1695
1918
  }
1696
1919
  const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
1697
1920
  const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
1698
- if (angle === undefined) return displayText;
1921
+ if (angle === undefined) {
1922
+ return displayText;
1923
+ }
1699
1924
  const roundedAngle = src.utils.roundNumber(angle, 2);
1700
1925
  displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
1701
1926
  return displayText;
@@ -1839,7 +2064,6 @@ function PlanarFreehandROI_getDisplayText(mappedAnnotations) {
1839
2064
 
1840
2065
 
1841
2066
 
1842
-
1843
2067
  const RectangleROI = {
1844
2068
  toAnnotation: measurement => {},
1845
2069
  toMeasurement: (csToolsEventDetail, DisplaySetService, CornerstoneViewportService, getValueTypeFromToolType) => {
@@ -1937,20 +2161,22 @@ function RectangleROI_getMappedAnnotations(annotation, DisplaySetService) {
1937
2161
  stdDev,
1938
2162
  max,
1939
2163
  area,
1940
- Modality
2164
+ Modality,
2165
+ modalityUnit,
2166
+ areaUnit
1941
2167
  } = targetStats;
1942
- const unit = utils_getModalityUnit(Modality);
1943
2168
  annotations.push({
1944
2169
  SeriesInstanceUID,
1945
2170
  SOPInstanceUID,
1946
2171
  SeriesNumber,
1947
2172
  frameNumber,
1948
2173
  Modality,
1949
- unit,
2174
+ unit: modalityUnit,
1950
2175
  mean,
1951
2176
  stdDev,
1952
2177
  max,
1953
- area
2178
+ area,
2179
+ areaUnit
1954
2180
  });
1955
2181
  });
1956
2182
  return annotations;
@@ -1974,13 +2200,14 @@ function RectangleROI_getReport(mappedAnnotations, points, FrameOfReferenceUID)
1974
2200
  stdDev,
1975
2201
  max,
1976
2202
  area,
1977
- unit
2203
+ unit,
2204
+ areaUnit
1978
2205
  } = annotation;
1979
2206
  if (!mean || !unit || !max || !area) {
1980
2207
  return;
1981
2208
  }
1982
- columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
1983
- values.push(max, mean, stdDev, area);
2209
+ columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit');
2210
+ values.push(max, mean, stdDev, unit, area, areaUnit);
1984
2211
  });
1985
2212
  if (FrameOfReferenceUID) {
1986
2213
  columns.push('FrameOfReferenceUID');
@@ -2008,7 +2235,8 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2008
2235
  const {
2009
2236
  area,
2010
2237
  SOPInstanceUID,
2011
- frameNumber
2238
+ frameNumber,
2239
+ areaUnit
2012
2240
  } = mappedAnnotations[0];
2013
2241
  const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
2014
2242
  let InstanceNumber;
@@ -2020,7 +2248,7 @@ function RectangleROI_getDisplayText(mappedAnnotations, displaySet) {
2020
2248
 
2021
2249
  // Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
2022
2250
  const roundedArea = src.utils.roundNumber(area || 0, 2);
2023
- displayText.push(`${roundedArea} mm<sup>2</sup>`);
2251
+ displayText.push(`${roundedArea} ${areaUnit}`);
2024
2252
 
2025
2253
  // Todo: we need a better UI for displaying all these information
2026
2254
  mappedAnnotations.forEach(mappedAnnotation => {
@@ -2515,7 +2743,7 @@ function getInterleavedFrames(imageIds) {
2515
2743
  }
2516
2744
  while (!prefetchQueuedFilled.currentPositionDownToMinimum || !prefetchQueuedFilled.currentPositionUpToMaximum) {
2517
2745
  if (!prefetchQueuedFilled.currentPositionDownToMinimum) {
2518
- // Add imageId bellow
2746
+ // Add imageId below
2519
2747
  lowerImageIdIndex--;
2520
2748
  imageIdsToPrefetch.push({
2521
2749
  imageId: imageIds[lowerImageIdIndex],
@@ -2540,7 +2768,7 @@ function getInterleavedFrames(imageIds) {
2540
2768
  return imageIdsToPrefetch;
2541
2769
  }
2542
2770
  // EXTERNAL MODULE: ../../../node_modules/lodash/lodash.js
2543
- var lodash = __webpack_require__(92891);
2771
+ var lodash = __webpack_require__(44379);
2544
2772
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/interleaveCenterLoader.ts
2545
2773
 
2546
2774
 
@@ -2730,8 +2958,12 @@ function getNthFrames(imageIds) {
2730
2958
  * @returns [] reordered to be breadth first traversal of lists
2731
2959
  */
2732
2960
  function interleave(lists) {
2733
- if (!lists || !lists.length) return [];
2734
- if (lists.length === 1) return lists[0];
2961
+ if (!lists || !lists.length) {
2962
+ return [];
2963
+ }
2964
+ if (lists.length === 1) {
2965
+ return lists[0];
2966
+ }
2735
2967
  console.time('interleave');
2736
2968
  const useLists = [...lists];
2737
2969
  const ret = [];
@@ -3004,6 +3236,7 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3004
3236
  commands: [{
3005
3237
  commandName: 'showCornerstoneContextMenu',
3006
3238
  commandOptions: {
3239
+ requireNearbyToolData: true,
3007
3240
  menuId: 'measurementsContextMenu'
3008
3241
  }
3009
3242
  }]
@@ -3020,9 +3253,15 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
3020
3253
  function getEventName(evt) {
3021
3254
  const button = evt.detail.event.which;
3022
3255
  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');
3256
+ if (evt.detail.event.altKey) {
3257
+ nameArr.push('alt');
3258
+ }
3259
+ if (evt.detail.event.ctrlKey) {
3260
+ nameArr.push('ctrl');
3261
+ }
3262
+ if (evt.detail.event.shiftKey) {
3263
+ nameArr.push('shift');
3264
+ }
3026
3265
  nameArr.push('button');
3027
3266
  nameArr.push(button);
3028
3267
  return nameArr.join('');
@@ -3040,9 +3279,18 @@ function initContextMenu(_ref) {
3040
3279
  const cornerstoneViewportHandleEvent = (name, evt) => {
3041
3280
  const customizations = customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
3042
3281
  const toRun = customizations[name];
3043
- console.log('initContextMenu::cornerstoneViewportHandleEvent', name, toRun);
3282
+ if (!toRun) {
3283
+ return;
3284
+ }
3285
+
3286
+ // only find nearbyToolData if required, for the click (which closes the context menu
3287
+ // we don't need to find nearbyToolData)
3288
+ let nearbyToolData = null;
3289
+ if (toRun.commands.some(command => command.commandOptions?.requireNearbyToolData)) {
3290
+ nearbyToolData = findNearbyToolData(commandsManager, evt);
3291
+ }
3044
3292
  const options = {
3045
- nearbyToolData: findNearbyToolData(commandsManager, evt),
3293
+ nearbyToolData,
3046
3294
  event: evt
3047
3295
  };
3048
3296
  commandsManager.run(toRun, options);
@@ -3057,10 +3305,11 @@ function initContextMenu(_ref) {
3057
3305
  element
3058
3306
  } = evt.detail;
3059
3307
  const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
3060
- if (!viewportInfo) return;
3061
- const viewportIndex = viewportInfo.getViewportIndex();
3308
+ if (!viewportInfo) {
3309
+ return;
3310
+ }
3062
3311
  // TODO check update upstream
3063
- (0,state/* setEnabledElement */.Yc)(viewportIndex, element);
3312
+ (0,state/* setEnabledElement */.Yc)(viewportId, element);
3064
3313
  element.addEventListener(cs3DToolsEvents.MOUSE_CLICK, cornerstoneViewportHandleClick);
3065
3314
  }
3066
3315
  function elementDisabledHandler(evt) {
@@ -3094,9 +3343,15 @@ const DEFAULT_DOUBLE_CLICK = {
3094
3343
  */
3095
3344
  function getDoubleClickEventName(evt) {
3096
3345
  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');
3346
+ if (evt.detail.event.altKey) {
3347
+ nameArr.push('alt');
3348
+ }
3349
+ if (evt.detail.event.ctrlKey) {
3350
+ nameArr.push('ctrl');
3351
+ }
3352
+ if (evt.detail.event.shiftKey) {
3353
+ nameArr.push('shift');
3354
+ }
3100
3355
  nameArr.push('doubleClick');
3101
3356
  return nameArr.join('');
3102
3357
  }
@@ -3154,7 +3409,6 @@ function initDoubleClick(_ref) {
3154
3409
 
3155
3410
 
3156
3411
 
3157
-
3158
3412
  // TODO: Cypress tests are currently grabbing this from the window?
3159
3413
  window.cornerstone = esm;
3160
3414
  window.cornerstoneTools = dist_esm;
@@ -3169,7 +3423,24 @@ async function init(_ref) {
3169
3423
  configuration,
3170
3424
  appConfig
3171
3425
  } = _ref;
3172
- await (0,esm.init)();
3426
+ // Note: this should run first before initializing the cornerstone
3427
+ // DO NOT CHANGE THE ORDER
3428
+ const value = appConfig.useSharedArrayBuffer;
3429
+ let sharedArrayBufferDisabled = false;
3430
+ if (value === 'AUTO') {
3431
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.AUTO);
3432
+ } else if (value === 'FALSE' || value === false) {
3433
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.FALSE);
3434
+ sharedArrayBufferDisabled = true;
3435
+ } else {
3436
+ esm.setUseSharedArrayBuffer(esm.Enums.SharedArrayBufferModes.TRUE);
3437
+ }
3438
+ await (0,esm.init)({
3439
+ rendering: {
3440
+ preferSizeOverAccuracy: Boolean(appConfig.use16BitDataType),
3441
+ useNorm16Texture: Boolean(appConfig.use16BitDataType)
3442
+ }
3443
+ });
3173
3444
 
3174
3445
  // For debugging e2e tests that are failing on CI
3175
3446
  esm.setUseCPURendering(Boolean(appConfig.useCPURendering));
@@ -3181,34 +3452,35 @@ async function init(_ref) {
3181
3452
  }
3182
3453
  });
3183
3454
 
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);
3455
+ // For debugging large datasets, otherwise prefer the defaults
3456
+ const {
3457
+ maxCacheSize
3458
+ } = appConfig;
3459
+ if (maxCacheSize) {
3460
+ esm.cache.setMaxCacheSize(maxCacheSize);
3461
+ }
3188
3462
  initCornerstoneTools();
3189
3463
  esm.Settings.getRuntimeSettings().set('useCursors', Boolean(appConfig.useCursors));
3190
3464
  const {
3191
3465
  userAuthenticationService,
3192
- measurementService,
3193
3466
  customizationService,
3194
- displaySetService,
3195
- uiDialogService,
3196
3467
  uiModalService,
3197
3468
  uiNotificationService,
3198
3469
  cineService,
3199
3470
  cornerstoneViewportService,
3200
3471
  hangingProtocolService,
3201
3472
  toolGroupService,
3473
+ toolbarService,
3202
3474
  viewportGridService,
3203
3475
  stateSyncService
3204
3476
  } = servicesManager.services;
3205
3477
  window.services = servicesManager.services;
3206
3478
  window.extensionManager = extensionManager;
3207
3479
  window.commandsManager = commandsManager;
3208
- if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated) {
3480
+ if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated && !sharedArrayBufferDisabled) {
3209
3481
  uiNotificationService.show({
3210
3482
  title: 'Cross Origin Isolation',
3211
- message: 'Cross Origin Isolation is not enabled, volume rendering will not work (e.g., MPR)',
3483
+ message: 'Cross Origin Isolation is not enabled, read more about it here: https://docs.ohif.org/faq/',
3212
3484
  type: 'warning'
3213
3485
  });
3214
3486
  }
@@ -3254,7 +3526,7 @@ async function init(_ref) {
3254
3526
  thumbnail: appConfig?.maxNumRequests?.thumbnail || 75,
3255
3527
  prefetch: appConfig?.maxNumRequests?.prefetch || 10
3256
3528
  };
3257
- initWADOImageLoader(userAuthenticationService, appConfig);
3529
+ initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
3258
3530
 
3259
3531
  /* Measurement Service */
3260
3532
  this.measurementServiceSource = connectToolsToMeasurementService(servicesManager);
@@ -3289,11 +3561,51 @@ async function init(_ref) {
3289
3561
  customizationService,
3290
3562
  commandsManager
3291
3563
  });
3292
- const newStackCallback = evt => {
3564
+
3565
+ /**
3566
+ * When a viewport gets a new display set, this call will go through all the
3567
+ * active tools in the toolbar, and call any commands registered in the
3568
+ * toolbar service with a callback to re-enable on displaying the viewport.
3569
+ */
3570
+ const toolbarEventListener = evt => {
3293
3571
  const {
3294
3572
  element
3295
3573
  } = evt.detail;
3296
- dist_esm.utilities.stackPrefetch.enable(element);
3574
+ const activeTools = toolbarService.getActiveTools();
3575
+ activeTools.forEach(tool => {
3576
+ const toolData = toolbarService.getNestedButton(tool);
3577
+ const commands = toolData?.listeners?.[evt.type];
3578
+ commandsManager.run(commands, {
3579
+ element,
3580
+ evt
3581
+ });
3582
+ });
3583
+ };
3584
+
3585
+ /** Listens for active viewport events and fires the toolbar listeners */
3586
+ const activeViewportEventListener = evt => {
3587
+ const {
3588
+ viewportId
3589
+ } = evt;
3590
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
3591
+ const activeTools = toolbarService.getActiveTools();
3592
+ activeTools.forEach(tool => {
3593
+ if (!toolGroup?._toolInstances?.[tool]) {
3594
+ return;
3595
+ }
3596
+
3597
+ // check if tool is active on the new viewport
3598
+ const toolEnabled = toolGroup._toolInstances[tool].mode === dist_esm.Enums.ToolModes.Enabled;
3599
+ if (!toolEnabled) {
3600
+ return;
3601
+ }
3602
+ const button = toolbarService.getNestedButton(tool);
3603
+ const commands = button?.listeners?.[evt.type];
3604
+ commandsManager.run(commands, {
3605
+ viewportId,
3606
+ evt
3607
+ });
3608
+ });
3297
3609
  };
3298
3610
  const resetCrosshairs = evt => {
3299
3611
  const {
@@ -3316,12 +3628,18 @@ async function init(_ref) {
3316
3628
  toolGroup.setToolEnabled('Crosshairs');
3317
3629
  }
3318
3630
  };
3631
+ esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, evt => {
3632
+ const {
3633
+ element
3634
+ } = evt.detail;
3635
+ dist_esm.utilities.stackContextPrefetch.enable(element);
3636
+ });
3319
3637
  function elementEnabledHandler(evt) {
3320
3638
  const {
3321
3639
  element
3322
3640
  } = evt.detail;
3323
3641
  element.addEventListener(esm.EVENTS.CAMERA_RESET, resetCrosshairs);
3324
- esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, newStackCallback);
3642
+ esm.eventTarget.addEventListener(esm.EVENTS.STACK_VIEWPORT_NEW_STACK, toolbarEventListener);
3325
3643
  }
3326
3644
  function elementDisabledHandler(evt) {
3327
3645
  const {
@@ -3338,31 +3656,7 @@ async function init(_ref) {
3338
3656
 
3339
3657
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
3340
3658
  esm.eventTarget.addEventListener(esm.EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
3341
- viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_INDEX_CHANGED, _ref2 => {
3342
- let {
3343
- viewportIndex,
3344
- viewportId
3345
- } = _ref2;
3346
- viewportId = viewportId || `viewport-${viewportIndex}`;
3347
- const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
3348
- if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
3349
- return;
3350
- }
3351
-
3352
- // check if reference lines are active
3353
- const referenceLinesEnabled = toolGroup._toolInstances['ReferenceLines'].mode === dist_esm.Enums.ToolModes.Enabled;
3354
- if (!referenceLinesEnabled) {
3355
- return;
3356
- }
3357
- toolGroup.setToolConfiguration(dist_esm.ReferenceLinesTool.toolName, {
3358
- sourceViewportId: viewportId
3359
- }, true // overwrite
3360
- );
3361
-
3362
- // make sure to set it to enabled again since we want to recalculate
3363
- // the source-target lines
3364
- toolGroup.setToolEnabled(dist_esm.ReferenceLinesTool.toolName);
3365
- });
3659
+ viewportGridService.subscribe(viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, activeViewportEventListener);
3366
3660
  }
3367
3661
  function CPUModal() {
3368
3662
  return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement("p", null, "Your computer does not have enough GPU power to support the default GPU rendering mode. OHIF has switched to CPU rendering mode. Please note that CPU rendering does not support all features such as Volume Rendering, Multiplanar Reconstruction, and Segmentation Overlays."));
@@ -3387,12 +3681,12 @@ function _showCPURenderingModal(uiModalService, hangingProtocolService) {
3387
3681
  });
3388
3682
  }
3389
3683
  // EXTERNAL MODULE: ../../../node_modules/react-dropzone/dist/es/index.js + 5 modules
3390
- var es = __webpack_require__(58591);
3684
+ var es = __webpack_require__(74834);
3391
3685
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
3392
- var prop_types = __webpack_require__(60216);
3686
+ var prop_types = __webpack_require__(3827);
3393
3687
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
3394
3688
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
3395
- var classnames = __webpack_require__(40841);
3689
+ var classnames = __webpack_require__(44921);
3396
3690
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
3397
3691
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/DicomFileUploader.ts
3398
3692
 
@@ -3541,7 +3835,9 @@ class DicomFileUploader extends src/* PubSubService */.hC {
3541
3835
  request.addEventListener('loadend', cleanUpCallback);
3542
3836
  }
3543
3837
  _checkDicomFile(arrayBuffer) {
3544
- if (arrayBuffer.length <= 132) return false;
3838
+ if (arrayBuffer.length <= 132) {
3839
+ return false;
3840
+ }
3545
3841
  const arr = new Uint8Array(arrayBuffer.slice(128, 132));
3546
3842
  // bytes from 128 to 132 must be "DICM"
3547
3843
  return Array.from('DICM').every((char, i) => char.charCodeAt(0) === arr[i]);
@@ -3560,7 +3856,6 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3560
3856
  const [percentComplete, setPercentComplete] = (0,react.useState)(dicomFileUploader.getPercentComplete());
3561
3857
  const [failedReason, setFailedReason] = (0,react.useState)('');
3562
3858
  const [status, setStatus] = (0,react.useState)(dicomFileUploader.getStatus());
3563
- console.info(`${dicomFileUploader.getFileId()}`);
3564
3859
  const isComplete = (0,react.useCallback)(() => {
3565
3860
  return status === UploadStatus.Failed || status === UploadStatus.Cancelled || status === UploadStatus.Success;
3566
3861
  }, [status]);
@@ -3601,25 +3896,25 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(_ref => {
3601
3896
  }
3602
3897
  };
3603
3898
  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"
3899
+ className: "min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg"
3605
3900
  }, /*#__PURE__*/react.createElement("div", {
3606
- className: "flex flex-col gap-1 self-top w-0 grow shrink"
3901
+ className: "self-top flex w-0 shrink grow flex-col gap-1"
3607
3902
  }, /*#__PURE__*/react.createElement("div", {
3608
3903
  className: "flex gap-4"
3609
3904
  }, /*#__PURE__*/react.createElement("div", {
3610
- className: "flex w-6 justify-center items-center shrink-0"
3905
+ className: "flex w-6 shrink-0 items-center justify-center"
3611
3906
  }, getStatusIcon()), /*#__PURE__*/react.createElement("div", {
3612
- className: "text-ellipsis whitespace-nowrap overflow-hidden"
3907
+ className: "overflow-hidden text-ellipsis whitespace-nowrap"
3613
3908
  }, dicomFileUploader.getFileName())), failedReason && /*#__PURE__*/react.createElement("div", {
3614
3909
  className: "pl-10"
3615
3910
  }, failedReason)), /*#__PURE__*/react.createElement("div", {
3616
- className: "w-24 flex items-center"
3911
+ className: "flex w-24 items-center"
3617
3912
  }, !isComplete() && /*#__PURE__*/react.createElement(react.Fragment, null, dicomFileUploader.getStatus() === UploadStatus.InProgress && /*#__PURE__*/react.createElement("div", {
3618
3913
  className: "w-10 text-right"
3619
3914
  }, percentComplete, "%"), /*#__PURE__*/react.createElement("div", {
3620
- className: "flex cursor-pointer ml-auto"
3915
+ className: "ml-auto flex cursor-pointer"
3621
3916
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
3622
- className: "self-center text-primary-active",
3917
+ className: "text-primary-active self-center",
3623
3918
  name: "close",
3624
3919
  onClick: cancelUpload
3625
3920
  })))));
@@ -3846,7 +4141,7 @@ function DicomUploadProgress(_ref) {
3846
4141
  }, []);
3847
4142
  const getNumCompletedAndTimeRemainingComponent = () => {
3848
4143
  return /*#__PURE__*/react.createElement("div", {
3849
- className: "text-lg px-1 pb-4 h-14 flex bg-primary-dark items-center"
4144
+ className: "bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg"
3850
4145
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("span", {
3851
4146
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3852
4147
  }, `${dicomFileUploaderArr.length} ${dicomFileUploaderArr.length > 1 ? 'files' : 'file'} completed.`), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
@@ -3861,13 +4156,13 @@ function DicomUploadProgress(_ref) {
3861
4156
  }, ' files completed.', "\xA0"), /*#__PURE__*/react.createElement("span", {
3862
4157
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
3863
4158
  }, 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'),
4159
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES, 'text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'),
3865
4160
  onClick: cancelAllUploads
3866
4161
  }, "Cancel All Uploads")));
3867
4162
  };
3868
4163
  const getShowFailedOnlyIconComponent = () => {
3869
4164
  return /*#__PURE__*/react.createElement("div", {
3870
- className: "ml-auto flex justify-center w-6"
4165
+ className: "ml-auto flex w-6 justify-center"
3871
4166
  }, numFails > 0 && /*#__PURE__*/react.createElement("div", {
3872
4167
  onClick: () => setShowFailedOnly(currentShowFailedOnly => !currentShowFailedOnly)
3873
4168
  }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
@@ -3877,28 +4172,28 @@ function DicomUploadProgress(_ref) {
3877
4172
  };
3878
4173
  const getPercentCompleteComponent = () => {
3879
4174
  return /*#__PURE__*/react.createElement("div", {
3880
- className: "overflow-y-scroll ohif-scrollbar px-2 border-b border-secondary-light"
4175
+ className: "ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2"
3881
4176
  }, /*#__PURE__*/react.createElement("div", {
3882
- className: "flex w-full p-2.5 items-center min-h-14"
4177
+ className: "min-h-14 flex w-full items-center p-2.5"
3883
4178
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3884
- className: "text-xl text-primary-light"
4179
+ className: "text-primary-light text-xl"
3885
4180
  }, numFails > 0 ? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!` : 'Completed!'), getShowFailedOnlyIconComponent()) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
3886
4181
  ref: progressBarContainerRef,
3887
4182
  className: "flex-grow"
3888
4183
  }, /*#__PURE__*/react.createElement(ui_src/* ProgressLoadingBar */.YE, {
3889
4184
  progress: showInfiniteProgressBar() ? undefined : Math.min(100, percentComplete)
3890
4185
  })), /*#__PURE__*/react.createElement("div", {
3891
- className: "w-24 ml-1 flex items-center"
4186
+ className: "ml-1 flex w-24 items-center"
3892
4187
  }, /*#__PURE__*/react.createElement("div", {
3893
4188
  className: "w-10 text-right"
3894
4189
  }, `${getPercentCompleteRounded()}%`), getShowFailedOnlyIconComponent()))));
3895
4190
  };
3896
4191
  return /*#__PURE__*/react.createElement("div", {
3897
- className: "flex flex-col grow"
4192
+ className: "flex grow flex-col"
3898
4193
  }, getNumCompletedAndTimeRemainingComponent(), /*#__PURE__*/react.createElement("div", {
3899
- className: "flex flex-col bg-black text-lg overflow-hidden grow"
4194
+ className: "flex grow flex-col overflow-hidden bg-black text-lg"
3900
4195
  }, getPercentCompleteComponent(), /*#__PURE__*/react.createElement("div", {
3901
- className: "overflow-y-scroll ohif-scrollbar px-2 grow h-1"
4196
+ className: "ohif-scrollbar h-1 grow overflow-y-scroll px-2"
3902
4197
  }, dicomFileUploaderArr.filter(dicomFileUploader => !showFailedOnly || dicomFileUploader.getStatus() === UploadStatus.Failed).map(dicomFileUploader => /*#__PURE__*/react.createElement(DicomUpload_DicomUploadProgressItem, {
3903
4198
  key: dicomFileUploader.getFileId(),
3904
4199
  dicomFileUploader: dicomFileUploader
@@ -3945,7 +4240,7 @@ function DicomUpload(_ref) {
3945
4240
  getRootProps
3946
4241
  } = _ref2;
3947
4242
  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"
4243
+ className: "dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
3949
4244
  }), /*#__PURE__*/react.createElement("div", {
3950
4245
  className: "flex gap-3"
3951
4246
  }, /*#__PURE__*/react.createElement(es/* default */.Z, {
@@ -3969,7 +4264,7 @@ function DicomUpload(_ref) {
3969
4264
  getInputProps
3970
4265
  } = _ref4;
3971
4266
  return /*#__PURE__*/react.createElement("div", getRootProps(), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
3972
- type: ui_src/* ButtonEnums.type */.LZ.U.secondary,
4267
+ type: ui_src/* ButtonEnums.type */.LZ.dt.secondary,
3973
4268
  disabled: false,
3974
4269
  onClick: () => {}
3975
4270
  }, 'Add folder', /*#__PURE__*/react.createElement("input", _extends({}, getInputProps(), {
@@ -3979,7 +4274,7 @@ function DicomUpload(_ref) {
3979
4274
  })), /*#__PURE__*/react.createElement("div", {
3980
4275
  className: "pt-5"
3981
4276
  }, "or drag images or folders here"), /*#__PURE__*/react.createElement("div", {
3982
- className: "pt-3 text-aqua-pale text-lg"
4277
+ className: "text-aqua-pale pt-3 text-lg"
3983
4278
  }, "(DICOM files supported)"));
3984
4279
  });
3985
4280
  };
@@ -4043,7 +4338,7 @@ function getCustomizationModule() {
4043
4338
  }
4044
4339
  /* harmony default export */ const src_getCustomizationModule = (getCustomizationModule);
4045
4340
  // EXTERNAL MODULE: ../../../node_modules/html2canvas/dist/html2canvas.esm.js
4046
- var html2canvas_esm = __webpack_require__(63691);
4341
+ var html2canvas_esm = __webpack_require__(76010);
4047
4342
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
4048
4343
 
4049
4344
 
@@ -4059,10 +4354,10 @@ const VIEWPORT_ID = 'cornerstone-viewport-download-form';
4059
4354
  const CornerstoneViewportDownloadForm = _ref => {
4060
4355
  let {
4061
4356
  onClose,
4062
- activeViewportIndex,
4357
+ activeViewportId: activeViewportIdProp,
4063
4358
  cornerstoneViewportService
4064
4359
  } = _ref;
4065
- const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIndex);
4360
+ const enabledElement = (0,state/* getEnabledElement */.K8)(activeViewportIdProp);
4066
4361
  const activeViewportElement = enabledElement?.element;
4067
4362
  const activeViewportEnabledElement = (0,esm.getEnabledElement)(activeViewportElement);
4068
4363
  const {
@@ -4181,13 +4476,18 @@ const CornerstoneViewportDownloadForm = _ref => {
4181
4476
  const imageId = viewport.getCurrentImageId();
4182
4477
  const properties = viewport.getProperties();
4183
4478
  downloadViewport.setStack([imageId]).then(() => {
4184
- downloadViewport.setProperties(properties);
4185
- const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
4186
- const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
4187
- resolve({
4188
- width: newWidth,
4189
- height: newHeight
4190
- });
4479
+ try {
4480
+ downloadViewport.setProperties(properties);
4481
+ const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
4482
+ const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
4483
+ resolve({
4484
+ width: newWidth,
4485
+ height: newHeight
4486
+ });
4487
+ } catch (e) {
4488
+ // Happens on clicking the cancel button
4489
+ console.warn('Unable to set properties', e);
4490
+ }
4191
4491
  });
4192
4492
  } else if (downloadViewport instanceof esm.VolumeViewport) {
4193
4493
  const actors = viewport.getActors();
@@ -4264,74 +4564,75 @@ const CornerstoneViewportDownloadForm = _ref => {
4264
4564
  };
4265
4565
  CornerstoneViewportDownloadForm.propTypes = {
4266
4566
  onClose: (prop_types_default()).func,
4267
- activeViewportIndex: (prop_types_default()).number.isRequired
4567
+ activeViewportId: (prop_types_default()).string.isRequired
4268
4568
  };
4269
4569
  /* harmony default export */ const utils_CornerstoneViewportDownloadForm = (CornerstoneViewportDownloadForm);
4270
- ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/stackSync/calculateViewportRegistrations.ts
4271
-
4272
- function calculateViewportRegistrations(viewports) {
4273
- const viewportPairs = _getViewportPairs(viewports);
4274
- for (const [viewport, nextViewport] of viewportPairs) {
4275
- // check if they are in the same Frame of Reference
4276
- const renderingEngine1 = (0,esm.getRenderingEngine)(viewport.renderingEngineId);
4277
- const renderingEngine2 = (0,esm.getRenderingEngine)(nextViewport.renderingEngineId);
4278
- const csViewport1 = renderingEngine1.getViewport(viewport.viewportId);
4279
- const csViewport2 = renderingEngine2.getViewport(nextViewport.viewportId);
4280
- esm.utilities.calculateViewportsSpatialRegistration(csViewport1, csViewport2);
4281
- }
4282
- }
4283
- const _getViewportPairs = viewports => {
4284
- const viewportPairs = [];
4285
- for (let i = 0; i < viewports.length; i++) {
4286
- for (let j = i + 1; j < viewports.length; j++) {
4287
- viewportPairs.push([viewports[i], viewports[j]]);
4288
- }
4289
- }
4290
- return viewportPairs;
4291
- };
4292
4570
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/stackSync/toggleStackImageSync.ts
4293
-
4294
-
4295
- // [ {
4296
- // synchronizerId: string,
4297
- // viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
4298
- // ]}
4299
- let STACK_IMAGE_SYNC_GROUPS_INFO = [];
4571
+ const STACK_SYNC_NAME = 'stackImageSync';
4300
4572
  function toggleStackImageSync(_ref) {
4301
4573
  let {
4302
4574
  toggledState,
4303
4575
  servicesManager,
4304
- getEnabledElement
4576
+ viewports: providedViewports
4305
4577
  } = _ref;
4578
+ if (!toggledState) {
4579
+ return disableSync(STACK_SYNC_NAME, servicesManager);
4580
+ }
4306
4581
  const {
4307
4582
  syncGroupService,
4308
4583
  viewportGridService,
4309
4584
  displaySetService,
4310
4585
  cornerstoneViewportService
4311
4586
  } = servicesManager.services;
4312
- if (!toggledState) {
4313
- STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
4314
- const {
4315
- viewports,
4316
- synchronizerId
4317
- } = syncGroupInfo;
4318
- viewports.forEach(_ref2 => {
4319
- let {
4320
- viewportId,
4321
- renderingEngineId
4322
- } = _ref2;
4323
- syncGroupService.removeViewportFromSyncGroup(viewportId, renderingEngineId, synchronizerId);
4324
- });
4587
+ const viewports = providedViewports || getReconstructableStackViewports(viewportGridService, displaySetService);
4588
+
4589
+ // create synchronization group and add the viewports to it.
4590
+ viewports.forEach(gridViewport => {
4591
+ const {
4592
+ viewportId
4593
+ } = gridViewport.viewportOptions;
4594
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
4595
+ if (!viewport) {
4596
+ return;
4597
+ }
4598
+ syncGroupService.addViewportToSyncGroup(viewportId, viewport.getRenderingEngine().id, {
4599
+ type: 'stackimage',
4600
+ id: STACK_SYNC_NAME,
4601
+ source: true,
4602
+ target: true
4325
4603
  });
4326
- return;
4327
- }
4328
- STACK_IMAGE_SYNC_GROUPS_INFO = [];
4604
+ });
4605
+ }
4606
+ function disableSync(syncName, servicesManager) {
4607
+ const {
4608
+ syncGroupService,
4609
+ viewportGridService,
4610
+ displaySetService,
4611
+ cornerstoneViewportService
4612
+ } = servicesManager.services;
4613
+ const viewports = getReconstructableStackViewports(viewportGridService, displaySetService);
4614
+ viewports.forEach(gridViewport => {
4615
+ const {
4616
+ viewportId
4617
+ } = gridViewport.viewportOptions;
4618
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
4619
+ if (!viewport) {
4620
+ return;
4621
+ }
4622
+ syncGroupService.removeViewportFromSyncGroup(viewport.id, viewport.getRenderingEngine().id, syncName);
4623
+ });
4624
+ }
4625
+ ;
4329
4626
 
4330
- // create synchronization groups and add viewports
4627
+ /**
4628
+ * Gets the consistent spacing stack viewport types, which are the ones which
4629
+ * can be navigated using the stack image sync right now.
4630
+ */
4631
+ function getReconstructableStackViewports(viewportGridService, displaySetService) {
4331
4632
  let {
4332
4633
  viewports
4333
4634
  } = viewportGridService.getState();
4334
-
4635
+ viewports = [...viewports.values()];
4335
4636
  // filter empty viewports
4336
4637
  viewports = viewports.filter(viewport => viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length);
4337
4638
 
@@ -4342,72 +4643,17 @@ function toggleStackImageSync(_ref) {
4342
4643
  } = viewport;
4343
4644
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
4344
4645
  const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
4646
+
4647
+ // TODO - add a better test than isReconstructable
4345
4648
  if (displaySet && displaySet.isReconstructable) {
4346
4649
  return true;
4347
4650
  }
4348
4651
  return false;
4349
4652
  }
4350
4653
  });
4351
- const viewportsByOrientation = viewports.reduce((acc, viewport) => {
4352
- const {
4353
- viewportId,
4354
- viewportType
4355
- } = viewport.viewportOptions;
4356
- if (viewportType !== 'stack') {
4357
- console.warn('Viewport is not a stack, cannot sync images yet');
4358
- return acc;
4359
- }
4360
- const {
4361
- element
4362
- } = cornerstoneViewportService.getViewportInfo(viewportId);
4363
- const {
4364
- viewport: csViewport,
4365
- renderingEngineId
4366
- } = getEnabledElement(element);
4367
- const {
4368
- viewPlaneNormal
4369
- } = csViewport.getCamera();
4370
-
4371
- // Should we round here? I guess so, but not sure how much precision we need
4372
- const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
4373
- if (!acc[orientation]) {
4374
- acc[orientation] = [];
4375
- }
4376
- acc[orientation].push({
4377
- viewportId,
4378
- renderingEngineId
4379
- });
4380
- return acc;
4381
- }, {});
4382
-
4383
- // create synchronizer for each group
4384
- Object.values(viewportsByOrientation).map(viewports => {
4385
- let synchronizerId = viewports.map(_ref3 => {
4386
- let {
4387
- viewportId
4388
- } = _ref3;
4389
- return viewportId;
4390
- }).join(',');
4391
- synchronizerId = `imageSync_${synchronizerId}`;
4392
- calculateViewportRegistrations(viewports);
4393
- viewports.forEach(_ref4 => {
4394
- let {
4395
- viewportId,
4396
- renderingEngineId
4397
- } = _ref4;
4398
- syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, {
4399
- type: 'stackimage',
4400
- id: synchronizerId,
4401
- source: true,
4402
- target: true
4403
- });
4404
- });
4405
- STACK_IMAGE_SYNC_GROUPS_INFO.push({
4406
- synchronizerId,
4407
- viewports
4408
- });
4409
- });
4654
+ return viewports;
4410
4655
  }
4656
+ ;
4411
4657
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/selection.ts
4412
4658
 
4413
4659
 
@@ -4457,6 +4703,7 @@ function commandsModule(_ref) {
4457
4703
  toolGroupService,
4458
4704
  cineService,
4459
4705
  toolbarService,
4706
+ stateSyncService,
4460
4707
  uiDialogService,
4461
4708
  cornerstoneViewportService,
4462
4709
  uiNotificationService,
@@ -4650,8 +4897,7 @@ function commandsModule(_ref) {
4650
4897
  console.warn('No viewport found for viewportId:', viewportId);
4651
4898
  return;
4652
4899
  }
4653
- const viewportIndex = viewportInfo.getViewportIndex();
4654
- viewportGridService.setActiveViewportIndex(viewportIndex);
4900
+ viewportGridService.setActiveViewportId(viewportId);
4655
4901
  },
4656
4902
  arrowTextCallback: _ref7 => {
4657
4903
  let {
@@ -4716,10 +4962,16 @@ function commandsModule(_ref) {
4716
4962
  toolbarServiceRecordInteraction: props => {
4717
4963
  toolbarService.recordInteraction(props);
4718
4964
  },
4965
+ // Enable or disable a toggleable command, without calling the activation
4966
+ // Used to setup already active tools from hanging protocols
4967
+ setToolbarToggled: props => {
4968
+ toolbarService.setToggled(props.toolId, props.isActive ?? true);
4969
+ },
4719
4970
  setToolActive: _ref9 => {
4720
4971
  let {
4721
4972
  toolName,
4722
- toolGroupId = null
4973
+ toolGroupId = null,
4974
+ toggledState
4723
4975
  } = _ref9;
4724
4976
  if (toolName === 'Crosshairs') {
4725
4977
  const activeViewportToolGroup = toolGroupService.getToolGroup(null);
@@ -4735,9 +4987,10 @@ function commandsModule(_ref) {
4735
4987
  }
4736
4988
  const {
4737
4989
  viewports
4738
- } = viewportGridService.getState() || {
4739
- viewports: []
4740
- };
4990
+ } = viewportGridService.getState();
4991
+ if (!viewports.size) {
4992
+ return;
4993
+ }
4741
4994
  const toolGroup = toolGroupService.getToolGroup(toolGroupId);
4742
4995
  const toolGroupViewportIds = toolGroup?.getViewportIds?.();
4743
4996
 
@@ -4745,11 +4998,8 @@ function commandsModule(_ref) {
4745
4998
  if (!toolGroupViewportIds || !toolGroupViewportIds.length) {
4746
4999
  return;
4747
5000
  }
4748
- const filteredViewports = viewports.filter(viewport => {
4749
- if (!viewport.viewportOptions) {
4750
- return false;
4751
- }
4752
- return toolGroupViewportIds.includes(viewport.viewportOptions.viewportId);
5001
+ const filteredViewports = Array.from(viewports.values()).filter(viewport => {
5002
+ return toolGroupViewportIds.includes(viewport.viewportId);
4753
5003
  });
4754
5004
  if (!filteredViewports.length) {
4755
5005
  return;
@@ -4773,6 +5023,14 @@ function commandsModule(_ref) {
4773
5023
  toolGroup.setToolPassive(activeToolName);
4774
5024
  }
4775
5025
  }
5026
+
5027
+ // If there is a toggle state, then simply set the enabled/disabled state without
5028
+ // setting the tool active.
5029
+ if (toggledState != null) {
5030
+ toggledState ? toolGroup.setToolEnabled(toolName) : toolGroup.setToolDisabled(toolName);
5031
+ return;
5032
+ }
5033
+
4776
5034
  // Set the new toolName to be active
4777
5035
  toolGroup.setToolActive(toolName, {
4778
5036
  bindings: [{
@@ -4782,9 +5040,9 @@ function commandsModule(_ref) {
4782
5040
  },
4783
5041
  showDownloadViewportModal: () => {
4784
5042
  const {
4785
- activeViewportIndex
5043
+ activeViewportId
4786
5044
  } = viewportGridService.getState();
4787
- if (!cornerstoneViewportService.getCornerstoneViewportByIndex(activeViewportIndex)) {
5045
+ if (!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)) {
4788
5046
  // Cannot download a non-cornerstone viewport (image).
4789
5047
  uiNotificationService.show({
4790
5048
  title: 'Download Image',
@@ -4801,7 +5059,7 @@ function commandsModule(_ref) {
4801
5059
  content: utils_CornerstoneViewportDownloadForm,
4802
5060
  title: 'Download High Quality Image',
4803
5061
  contentProps: {
4804
- activeViewportIndex,
5062
+ activeViewportId,
4805
5063
  onClose: uiModalService.hide,
4806
5064
  cornerstoneViewportService
4807
5065
  }
@@ -4882,15 +5140,13 @@ function commandsModule(_ref) {
4882
5140
  const {
4883
5141
  viewport
4884
5142
  } = 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
- }
5143
+ const {
5144
+ invert
5145
+ } = viewport.getProperties();
5146
+ viewport.setProperties({
5147
+ invert: !invert
5148
+ });
5149
+ viewport.render();
4894
5150
  },
4895
5151
  resetViewport: () => {
4896
5152
  const enabledElement = _getActiveViewportEnabledElement();
@@ -4993,12 +5249,12 @@ function commandsModule(_ref) {
4993
5249
  },
4994
5250
  setViewportColormap: _ref15 => {
4995
5251
  let {
4996
- viewportIndex,
5252
+ viewportId,
4997
5253
  displaySetInstanceUID,
4998
5254
  colormap,
4999
5255
  immediate = false
5000
5256
  } = _ref15;
5001
- const viewport = cornerstoneViewportService.getCornerstoneViewportByIndex(viewportIndex);
5257
+ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
5002
5258
  const actorEntries = viewport.getActors();
5003
5259
  const actorEntry = actorEntries.find(actorEntry => {
5004
5260
  return actorEntry.uid.includes(displaySetInstanceUID);
@@ -5015,51 +5271,53 @@ function commandsModule(_ref) {
5015
5271
  viewport.render();
5016
5272
  }
5017
5273
  },
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: () => {
5274
+ changeActiveViewport: _ref16 => {
5275
+ let {
5276
+ direction = 1
5277
+ } = _ref16;
5027
5278
  const {
5028
- activeViewportIndex,
5279
+ activeViewportId,
5029
5280
  viewports
5030
5281
  } = viewportGridService.getState();
5031
- const nextViewportIndex = (activeViewportIndex - 1 + viewports.length) % viewports.length;
5032
- viewportGridService.setActiveViewportIndex(nextViewportIndex);
5282
+ const viewportIds = Array.from(viewports.keys());
5283
+ const currentIndex = viewportIds.indexOf(activeViewportId);
5284
+ const nextViewportIndex = (currentIndex + direction + viewportIds.length) % viewportIds.length;
5285
+ viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex]);
5033
5286
  },
5034
- toggleStackImageSync: _ref16 => {
5287
+ toggleStackImageSync: _ref17 => {
5035
5288
  let {
5036
5289
  toggledState
5037
- } = _ref16;
5290
+ } = _ref17;
5038
5291
  toggleStackImageSync({
5039
- getEnabledElement: esm.getEnabledElement,
5040
5292
  servicesManager,
5041
5293
  toggledState
5042
5294
  });
5043
5295
  },
5044
- toggleReferenceLines: _ref17 => {
5296
+ setSourceViewportForReferenceLinesTool: _ref18 => {
5045
5297
  let {
5046
- toggledState
5047
- } = _ref17;
5048
- const {
5049
- activeViewportIndex
5050
- } = viewportGridService.getState();
5051
- const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(activeViewportIndex);
5052
- const viewportId = viewportInfo.getViewportId();
5053
- const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
5054
- if (!toggledState) {
5055
- toolGroup.setToolDisabled(dist_esm.ReferenceLinesTool.toolName);
5298
+ toggledState,
5299
+ viewportId
5300
+ } = _ref18;
5301
+ if (!viewportId) {
5302
+ const {
5303
+ activeViewportId
5304
+ } = viewportGridService.getState();
5305
+ viewportId = activeViewportId;
5056
5306
  }
5307
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
5057
5308
  toolGroup.setToolConfiguration(dist_esm.ReferenceLinesTool.toolName, {
5058
5309
  sourceViewportId: viewportId
5059
5310
  }, true // overwrite
5060
5311
  );
5312
+ },
5061
5313
 
5062
- toolGroup.setToolEnabled(dist_esm.ReferenceLinesTool.toolName);
5314
+ storePresentation: _ref19 => {
5315
+ let {
5316
+ viewportId
5317
+ } = _ref19;
5318
+ cornerstoneViewportService.storePresentation({
5319
+ viewportId
5320
+ });
5063
5321
  }
5064
5322
  };
5065
5323
  const definitions = {
@@ -5114,10 +5372,13 @@ function commandsModule(_ref) {
5114
5372
  }
5115
5373
  },
5116
5374
  incrementActiveViewport: {
5117
- commandFn: actions.incrementActiveViewport
5375
+ commandFn: actions.changeActiveViewport
5118
5376
  },
5119
5377
  decrementActiveViewport: {
5120
- commandFn: actions.decrementActiveViewport
5378
+ commandFn: actions.changeActiveViewport,
5379
+ options: {
5380
+ direction: -1
5381
+ }
5121
5382
  },
5122
5383
  flipViewportHorizontal: {
5123
5384
  commandFn: actions.flipViewportHorizontal
@@ -5194,8 +5455,14 @@ function commandsModule(_ref) {
5194
5455
  toggleStackImageSync: {
5195
5456
  commandFn: actions.toggleStackImageSync
5196
5457
  },
5197
- toggleReferenceLines: {
5198
- commandFn: actions.toggleReferenceLines
5458
+ setSourceViewportForReferenceLinesTool: {
5459
+ commandFn: actions.setSourceViewportForReferenceLinesTool
5460
+ },
5461
+ storePresentation: {
5462
+ commandFn: actions.storePresentation
5463
+ },
5464
+ setToolbarToggled: {
5465
+ commandFn: actions.setToolbarToggled
5199
5466
  }
5200
5467
  };
5201
5468
  return {
@@ -5210,9 +5477,8 @@ const mpr = {
5210
5477
  id: 'mpr',
5211
5478
  name: 'Multi-Planar Reconstruction',
5212
5479
  locked: true,
5213
- hasUpdatedPriorsInformation: false,
5214
5480
  createdDate: '2021-02-23',
5215
- modifiedDate: '2023-04-03',
5481
+ modifiedDate: '2023-08-15',
5216
5482
  availableTo: {},
5217
5483
  editableBy: {},
5218
5484
  // Unknown number of priors referenced - so just match any study
@@ -5283,6 +5549,7 @@ const mpr = {
5283
5549
  },
5284
5550
  viewports: [{
5285
5551
  viewportOptions: {
5552
+ viewportId: 'mpr-axial',
5286
5553
  toolGroupId: 'mpr',
5287
5554
  viewportType: 'volume',
5288
5555
  orientation: 'axial',
@@ -5301,6 +5568,7 @@ const mpr = {
5301
5568
  }]
5302
5569
  }, {
5303
5570
  viewportOptions: {
5571
+ viewportId: 'mpr-sagittal',
5304
5572
  toolGroupId: 'mpr',
5305
5573
  viewportType: 'volume',
5306
5574
  orientation: 'sagittal',
@@ -5319,6 +5587,7 @@ const mpr = {
5319
5587
  }]
5320
5588
  }, {
5321
5589
  viewportOptions: {
5590
+ viewportId: 'mpr-coronal',
5322
5591
  toolGroupId: 'mpr',
5323
5592
  viewportType: 'volume',
5324
5593
  orientation: 'coronal',
@@ -5341,7 +5610,6 @@ const mpr = {
5341
5610
  const mprAnd3DVolumeViewport = {
5342
5611
  id: 'mprAnd3DVolumeViewport',
5343
5612
  locked: true,
5344
- hasUpdatedPriorsInformation: false,
5345
5613
  name: 'mpr',
5346
5614
  createdDate: '2023-03-15T10:29:44.894Z',
5347
5615
  modifiedDate: '2023-03-15T10:29:44.894Z',
@@ -5530,9 +5798,9 @@ class ToolGroupService {
5530
5798
  return dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngine.id);
5531
5799
  }
5532
5800
  getActiveToolForViewport(viewportId) {
5533
- const toolGroup = dist_esm.ToolGroupManager.getToolGroupForViewport(viewportId);
5801
+ const toolGroup = this.getToolGroupForViewport(viewportId);
5534
5802
  if (!toolGroup) {
5535
- return null;
5803
+ return;
5536
5804
  }
5537
5805
  return toolGroup.getActivePrimaryMouseButtonTool();
5538
5806
  }
@@ -5595,9 +5863,8 @@ class ToolGroupService {
5595
5863
  this._setToolsMode(toolGroup, tools);
5596
5864
  }
5597
5865
  createToolGroupAndAddTools(toolGroupId, tools) {
5598
- let configs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
5599
5866
  const toolGroup = this.createToolGroup(toolGroupId);
5600
- this.addToolsToToolGroup(toolGroupId, tools, configs);
5867
+ this.addToolsToToolGroup(toolGroupId, tools);
5601
5868
  return toolGroup;
5602
5869
  }
5603
5870
 
@@ -5643,30 +5910,6 @@ class ToolGroupService {
5643
5910
  const toolInstance = toolGroup.getToolInstance(toolName);
5644
5911
  toolInstance.configuration = config;
5645
5912
  }
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
5913
  _setToolsMode(toolGroup, tools) {
5671
5914
  const {
5672
5915
  active,
@@ -5710,29 +5953,46 @@ class ToolGroupService {
5710
5953
  });
5711
5954
  }
5712
5955
  }
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
5956
+ _addTools(toolGroup, tools) {
5957
+ const addTools = tools => {
5958
+ tools.forEach(_ref5 => {
5959
+ let {
5960
+ toolName,
5961
+ parentTool,
5962
+ configuration
5963
+ } = _ref5;
5964
+ if (parentTool) {
5965
+ toolGroup.addToolInstance(toolName, parentTool, {
5966
+ ...configuration
5967
+ });
5968
+ } else {
5969
+ toolGroup.addTool(toolName, {
5970
+ ...configuration
5971
+ });
5972
+ }
5725
5973
  });
5726
- });
5974
+ };
5975
+ if (tools.active) {
5976
+ addTools(tools.active);
5977
+ }
5978
+ if (tools.passive) {
5979
+ addTools(tools.passive);
5980
+ }
5981
+ if (tools.enabled) {
5982
+ addTools(tools.enabled);
5983
+ }
5984
+ if (tools.disabled) {
5985
+ addTools(tools.disabled);
5986
+ }
5727
5987
  }
5728
5988
  }
5729
5989
  ToolGroupService.REGISTRATION = {
5730
5990
  name: 'toolGroupService',
5731
5991
  altName: 'ToolGroupService',
5732
- create: _ref5 => {
5992
+ create: _ref6 => {
5733
5993
  let {
5734
5994
  servicesManager
5735
- } = _ref5;
5995
+ } = _ref6;
5736
5996
  return new ToolGroupService(servicesManager);
5737
5997
  }
5738
5998
  };
@@ -5789,7 +6049,7 @@ class SyncGroupService {
5789
6049
  * @param type is the type of the synchronizer to create
5790
6050
  * @param creator
5791
6051
  */
5792
- setSynchronizer(type, creator) {
6052
+ addSynchronizerType(type, creator) {
5793
6053
  this.synchronizerCreators[type.toLowerCase()] = creator;
5794
6054
  }
5795
6055
  _getOrCreateSynchronizer(type, id, options) {
@@ -5867,10 +6127,10 @@ SyncGroupService.REGISTRATION = {
5867
6127
 
5868
6128
  /* harmony default export */ const services_SyncGroupService = (SyncGroupService);
5869
6129
  // EXTERNAL MODULE: ../../../node_modules/lodash.clonedeep/index.js
5870
- var lodash_clonedeep = __webpack_require__(71975);
6130
+ var lodash_clonedeep = __webpack_require__(11677);
5871
6131
  var lodash_clonedeep_default = /*#__PURE__*/__webpack_require__.n(lodash_clonedeep);
5872
6132
  // EXTERNAL MODULE: ../../../node_modules/lodash.isequal/index.js
5873
- var lodash_isequal = __webpack_require__(68652);
6133
+ var lodash_isequal = __webpack_require__(10311);
5874
6134
  var lodash_isequal_default = /*#__PURE__*/__webpack_require__.n(lodash_isequal);
5875
6135
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/transitions.ts
5876
6136
  /**
@@ -6026,6 +6286,82 @@ class SegmentationService extends src/* PubSubService */.hC {
6026
6286
  segmentation
6027
6287
  });
6028
6288
  };
6289
+ // Todo: this should not run on the main thread
6290
+ this.calculateCentroids = (segmentationId, segmentIndex) => {
6291
+ const segmentation = this.getSegmentation(segmentationId);
6292
+ const volume = this.getLabelmapVolume(segmentationId);
6293
+ const {
6294
+ dimensions,
6295
+ imageData
6296
+ } = volume;
6297
+ const scalarData = volume.getScalarData();
6298
+ const [dimX, dimY, numFrames] = dimensions;
6299
+ const frameLength = dimX * dimY;
6300
+ const segmentIndices = segmentIndex ? [segmentIndex] : segmentation.segments.filter(segment => segment?.segmentIndex).map(segment => segment.segmentIndex);
6301
+ const segmentIndicesSet = new Set(segmentIndices);
6302
+ const centroids = new Map();
6303
+ for (const index of segmentIndicesSet) {
6304
+ centroids.set(index, {
6305
+ x: 0,
6306
+ y: 0,
6307
+ z: 0,
6308
+ count: 0
6309
+ });
6310
+ }
6311
+ let voxelIndex = 0;
6312
+ for (let frame = 0; frame < numFrames; frame++) {
6313
+ for (let p = 0; p < frameLength; p++) {
6314
+ const segmentIndex = scalarData[voxelIndex++];
6315
+ if (segmentIndicesSet.has(segmentIndex)) {
6316
+ const centroid = centroids.get(segmentIndex);
6317
+ centroid.x += p % dimX;
6318
+ centroid.y += p / dimX | 0;
6319
+ centroid.z += frame;
6320
+ centroid.count++;
6321
+ }
6322
+ }
6323
+ }
6324
+ const result = new Map();
6325
+ for (const [index, centroid] of centroids) {
6326
+ const count = centroid.count;
6327
+ const normalizedCentroid = {
6328
+ x: centroid.x / count,
6329
+ y: centroid.y / count,
6330
+ z: centroid.z / count
6331
+ };
6332
+ normalizedCentroid.world = imageData.indexToWorld([normalizedCentroid.x, normalizedCentroid.y, normalizedCentroid.z]);
6333
+ result.set(index, normalizedCentroid);
6334
+ }
6335
+ this.setCentroids(segmentationId, result);
6336
+ return result;
6337
+ };
6338
+ this.setCentroids = (segmentationId, centroids) => {
6339
+ const segmentation = this.getSegmentation(segmentationId);
6340
+ const imageData = this.getLabelmapVolume(segmentationId).imageData; // Assuming this method returns imageData
6341
+
6342
+ if (!segmentation.cachedStats) {
6343
+ segmentation.cachedStats = {
6344
+ segmentCenter: {}
6345
+ };
6346
+ } else if (!segmentation.cachedStats.segmentCenter) {
6347
+ segmentation.cachedStats.segmentCenter = {};
6348
+ }
6349
+ for (const [segmentIndex, centroid] of centroids) {
6350
+ let world = centroid.world;
6351
+
6352
+ // If world coordinates are not provided, calculate them
6353
+ if (!world || world.length === 0) {
6354
+ world = imageData.indexToWorld(centroid.image);
6355
+ }
6356
+ segmentation.cachedStats.segmentCenter[segmentIndex] = {
6357
+ center: {
6358
+ image: centroid.image,
6359
+ world: world
6360
+ }
6361
+ };
6362
+ }
6363
+ this.addOrUpdateSegmentation(segmentation, true, true);
6364
+ };
6029
6365
  this.createSegmentationForDisplaySet = async (displaySetInstanceUID, options) => {
6030
6366
  const {
6031
6367
  displaySetService
@@ -6055,6 +6391,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6055
6391
  // We should set it as active by default, as it created for display
6056
6392
  isActive: true,
6057
6393
  type: representationType,
6394
+ FrameOfReferenceUID: options?.FrameOfReferenceUID || displaySet.instances?.[0]?.FrameOfReferenceUID,
6058
6395
  representationData: {
6059
6396
  LABELMAP: {
6060
6397
  volumeId: segmentationId,
@@ -6078,6 +6415,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6078
6415
  this.addSegmentationRepresentationToToolGroup = async function (toolGroupId, segmentationId) {
6079
6416
  let hydrateSegmentation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
6080
6417
  let representationType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : dist_esm.Enums.SegmentationRepresentations.Labelmap;
6418
+ let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6081
6419
  const segmentation = _this.getSegmentation(segmentationId);
6082
6420
  if (!segmentation) {
6083
6421
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
@@ -6122,12 +6460,17 @@ class SegmentationService extends src/* PubSubService */.hC {
6122
6460
  if (visibility !== undefined) {
6123
6461
  _this._setSegmentVisibility(segmentationId, segmentIndex, visibility, toolGroupId, suppressEvents);
6124
6462
  }
6125
- if (isLocked !== undefined) {
6463
+ if (isLocked) {
6126
6464
  _this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6127
6465
  }
6128
6466
  }
6467
+ if (!suppressEvents) {
6468
+ _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6469
+ segmentation
6470
+ });
6471
+ }
6129
6472
  };
6130
- this.setSegmentRGBAColorForSegmentation = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6473
+ this.setSegmentRGBAColor = (segmentationId, segmentIndex, rgbaColor, toolGroupId) => {
6131
6474
  const segmentation = this.getSegmentation(segmentationId);
6132
6475
  if (segmentation === undefined) {
6133
6476
  throw new Error(`no segmentation for segmentationId: ${segmentationId}`);
@@ -6153,6 +6496,9 @@ class SegmentationService extends src/* PubSubService */.hC {
6153
6496
  throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
6154
6497
  }
6155
6498
  segmentation.hydrated = true;
6499
+
6500
+ // Not all segmentations have dipslaysets, some of them are derived in the client
6501
+ _this._setDisplaySetIsHydrated(segmentationId, true);
6156
6502
  if (!suppressEvents) {
6157
6503
  _this._broadcastEvent(_this.EVENTS.SEGMENTATION_UPDATED, {
6158
6504
  segmentation
@@ -6467,34 +6813,40 @@ class SegmentationService extends src/* PubSubService */.hC {
6467
6813
  this._initSegmentationService();
6468
6814
  }
6469
6815
  /**
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
6816
+ * Adds a new segment to the specified segmentation.
6817
+ * @param segmentationId - The ID of the segmentation to add the segment to.
6818
+ * @param config - An object containing the configuration options for the new segment.
6819
+ * - segmentIndex: (optional) The index of the segment to add. If not provided, the next available index will be used.
6820
+ * - 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.
6821
+ * - properties: (optional) An object containing the properties of the new segment.
6822
+ * - label: (optional) The label of the new segment. If not provided, a default label will be used.
6823
+ * - color: (optional) The color of the new segment in RGB format. If not provided, a default color will be used.
6824
+ * - opacity: (optional) The opacity of the new segment. If not provided, a default opacity will be used.
6825
+ * - visibility: (optional) Whether the new segment should be visible. If not provided, the segment will be visible by default.
6826
+ * - isLocked: (optional) Whether the new segment should be locked for editing. If not provided, the segment will not be locked by default.
6827
+ * - 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
6828
  */
6483
- addSegment(segmentationId, segmentIndex, toolGroupId, properties) {
6484
- if (segmentIndex === 0) {
6829
+ addSegment(segmentationId) {
6830
+ let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6831
+ if (config?.segmentIndex === 0) {
6485
6832
  throw new Error('Segment index 0 is reserved for "no label"');
6486
6833
  }
6487
- toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
6834
+ const toolGroupId = config.toolGroupId ?? this._getFirstToolGroupId();
6488
6835
  const {
6489
6836
  segmentationRepresentationUID,
6490
6837
  segmentation
6491
6838
  } = this._getSegmentationInfo(segmentationId, toolGroupId);
6839
+ let segmentIndex = config.segmentIndex;
6840
+ if (!segmentIndex) {
6841
+ // grab the next available segment index
6842
+ segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
6843
+ }
6492
6844
  if (this._getSegmentInfo(segmentation, segmentIndex)) {
6493
6845
  throw new Error(`Segment ${segmentIndex} already exists`);
6494
6846
  }
6495
6847
  const rgbaColor = dist_esm.segmentation.config.color.getColorForSegmentIndex(toolGroupId, segmentationRepresentationUID, segmentIndex);
6496
6848
  segmentation.segments[segmentIndex] = {
6497
- label: properties.label,
6849
+ label: config.properties?.label ?? `Segment ${segmentIndex}`,
6498
6850
  segmentIndex: segmentIndex,
6499
6851
  color: [rgbaColor[0], rgbaColor[1], rgbaColor[2]],
6500
6852
  opacity: rgbaColor[3],
@@ -6502,15 +6854,18 @@ class SegmentationService extends src/* PubSubService */.hC {
6502
6854
  isLocked: false
6503
6855
  };
6504
6856
  segmentation.segmentCount++;
6857
+
6858
+ // make the newly added segment the active segment
6859
+ this._setActiveSegment(segmentationId, segmentIndex);
6505
6860
  const suppressEvents = true;
6506
- if (properties !== undefined) {
6861
+ if (config.properties !== undefined) {
6507
6862
  const {
6508
6863
  color: newColor,
6509
6864
  opacity,
6510
6865
  isLocked,
6511
6866
  visibility,
6512
6867
  active
6513
- } = properties;
6868
+ } = config.properties;
6514
6869
  if (newColor !== undefined) {
6515
6870
  this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
6516
6871
  }
@@ -6590,12 +6945,21 @@ class SegmentationService extends src/* PubSubService */.hC {
6590
6945
  let suppressEvents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
6591
6946
  this._setSegmentVisibility(segmentationId, segmentIndex, isVisible, toolGroupId, suppressEvents);
6592
6947
  }
6593
- setSegmentLockedForSegmentation(segmentationId, segmentIndex, isLocked) {
6948
+ setSegmentLocked(segmentationId, segmentIndex, isLocked) {
6594
6949
  const suppressEvents = false;
6595
6950
  this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
6596
6951
  }
6597
- setSegmentLabel(segmentationId, segmentIndex, segmentLabel) {
6598
- this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
6952
+
6953
+ /**
6954
+ * Toggles the locked state of a segment in a segmentation.
6955
+ * @param segmentationId - The ID of the segmentation.
6956
+ * @param segmentIndex - The index of the segment to toggle.
6957
+ */
6958
+ toggleSegmentLocked(segmentationId, segmentIndex) {
6959
+ const segmentation = this.getSegmentation(segmentationId);
6960
+ const segment = this._getSegmentInfo(segmentation, segmentIndex);
6961
+ const isLocked = !segment.isLocked;
6962
+ this._setSegmentLocked(segmentationId, segmentIndex, isLocked);
6599
6963
  }
6600
6964
  setSegmentColor(segmentationId, segmentIndex, color, toolGroupId) {
6601
6965
  this._setSegmentColor(segmentationId, segmentIndex, color, toolGroupId);
@@ -6608,7 +6972,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6608
6972
  const suppressEvents = false;
6609
6973
  this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
6610
6974
  }
6611
- setActiveSegmentForSegmentation(segmentationId, segmentIndex) {
6975
+ setActiveSegment(segmentationId, segmentIndex) {
6612
6976
  this._setActiveSegment(segmentationId, segmentIndex, false);
6613
6977
  }
6614
6978
 
@@ -6677,11 +7041,14 @@ class SegmentationService extends src/* PubSubService */.hC {
6677
7041
  }
6678
7042
  }]);
6679
7043
 
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);
7044
+ // if first segmentation, we can use the default colorLUT, otherwise
7045
+ // we need to generate a new one and use a new colorLUT
7046
+ const colorLUTIndex = 0;
7047
+ if (Object.keys(this.segmentations).length !== 0) {
7048
+ const newColorLUT = this.generateNewColorLUT();
7049
+ const colorLUTIndex = this.getNextColorLUTIndex();
7050
+ dist_esm.segmentation.config.color.addColorLUT(newColorLUT, colorLUTIndex);
7051
+ }
6685
7052
  this.segmentations[segmentationId] = {
6686
7053
  ...segmentation,
6687
7054
  label: segmentation.label || '',
@@ -6689,8 +7056,8 @@ class SegmentationService extends src/* PubSubService */.hC {
6689
7056
  activeSegmentIndex: segmentation.activeSegmentIndex ?? null,
6690
7057
  segmentCount: segmentation.segmentCount ?? 0,
6691
7058
  isActive: false,
6692
- colorLUTIndex: newColorLUTIndex,
6693
- isVisible: true
7059
+ isVisible: true,
7060
+ colorLUTIndex
6694
7061
  };
6695
7062
  cachedSegmentation = this.segmentations[segmentationId];
6696
7063
  this._updateCornerstoneSegmentations({
@@ -6715,6 +7082,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6715
7082
  id: segmentationId,
6716
7083
  displaySetInstanceUID: segDisplaySet.displaySetInstanceUID,
6717
7084
  type: representationType,
7085
+ label: segDisplaySet.SeriesDescription,
6718
7086
  representationData: {
6719
7087
  [LABELMAP]: {
6720
7088
  volumeId: segmentationId,
@@ -6731,11 +7099,11 @@ class SegmentationService extends src/* PubSubService */.hC {
6731
7099
  return this.addOrUpdateSegmentation(Object.assign(segmentation, cachedSegmentation), suppressEvents);
6732
7100
  }
6733
7101
  const {
6734
- segments,
7102
+ labelmapBufferArray,
6735
7103
  referencedVolumeId
6736
7104
  } = 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.');
7105
+ if (!labelmapBufferArray || !referencedVolumeId) {
7106
+ throw new Error('No labelmapBufferArray or referencedVolumeId found for the SEG displaySet');
6739
7107
  }
6740
7108
 
6741
7109
  // if the labelmap doesn't exist, we need to create it first from the
@@ -6754,72 +7122,27 @@ class SegmentationService extends src/* PubSubService */.hC {
6754
7122
  sharedArrayBuffer: true
6755
7123
  }
6756
7124
  });
6757
- const [rows, columns] = derivedVolume.dimensions;
6758
7125
  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) => {
7126
+ const segmentsInfo = segDisplaySet.segMetadata.data;
7127
+ derivedVolumeScalarData.set(new Uint8Array(labelmapBufferArray[0]));
7128
+ segmentation.segments = segmentsInfo.map((segmentInfo, segmentIndex) => {
7129
+ if (segmentIndex === 0) {
7130
+ return;
7131
+ }
6775
7132
  const {
6776
- pixelData: segPixelData
7133
+ SegmentedPropertyCategoryCodeSequence,
7134
+ SegmentNumber,
7135
+ SegmentLabel,
7136
+ SegmentAlgorithmType,
7137
+ SegmentAlgorithmName,
7138
+ SegmentedPropertyTypeCodeSequence,
7139
+ rgba
6777
7140
  } = 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);
7141
+ const {
7142
+ x,
7143
+ y,
7144
+ z
7145
+ } = segDisplaySet.centroids.get(segmentIndex);
6823
7146
  const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
6824
7147
  segmentation.cachedStats = {
6825
7148
  ...segmentation.cachedStats,
@@ -6834,51 +7157,24 @@ class SegmentationService extends src/* PubSubService */.hC {
6834
7157
  }
6835
7158
  }
6836
7159
  };
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],
7160
+ return {
7161
+ label: SegmentLabel || `Segment ${SegmentNumber}`,
7162
+ segmentIndex: Number(SegmentNumber),
7163
+ category: SegmentedPropertyCategoryCodeSequence ? SegmentedPropertyCategoryCodeSequence.CodeMeaning : '',
7164
+ type: SegmentedPropertyTypeCodeSequence ? SegmentedPropertyTypeCodeSequence.CodeMeaning : '',
7165
+ algorithmType: SegmentAlgorithmType,
7166
+ algorithmName: SegmentAlgorithmName,
7167
+ color: rgba,
7168
+ opacity: 255,
6873
7169
  isVisible: true,
6874
7170
  isLocked: false
6875
7171
  };
6876
7172
  });
7173
+ segmentation.segmentCount = segmentsInfo.length - 1;
6877
7174
  segDisplaySet.isLoaded = true;
6878
7175
  this._broadcastEvent(SegmentationService_EVENTS.SEGMENTATION_LOADING_COMPLETE, {
6879
7176
  segmentationId,
6880
- segDisplaySet,
6881
- overlappingSegments
7177
+ segDisplaySet
6882
7178
  });
6883
7179
  return this.addOrUpdateSegmentation(segmentation, suppressEvents);
6884
7180
  }
@@ -6911,6 +7207,7 @@ class SegmentationService extends src/* PubSubService */.hC {
6911
7207
  id: segmentationId,
6912
7208
  displaySetInstanceUID: rtDisplaySetUID,
6913
7209
  type: representationType,
7210
+ label: rtDisplaySet.SeriesDescription,
6914
7211
  representationData: {
6915
7212
  [CONTOUR]: {
6916
7213
  geometryIds
@@ -7015,6 +7312,9 @@ class SegmentationService extends src/* PubSubService */.hC {
7015
7312
  toolGroupService
7016
7313
  } = this.servicesManager.services;
7017
7314
  const center = this._getSegmentCenter(segmentationId, segmentIndex);
7315
+ if (!center?.world) {
7316
+ return;
7317
+ }
7018
7318
  const {
7019
7319
  world
7020
7320
  } = center;
@@ -7068,6 +7368,17 @@ class SegmentationService extends src/* PubSubService */.hC {
7068
7368
  const adjustedAlpha = type === LABELMAP ? alpha : 1 - alpha;
7069
7369
  highlightFn(segmentIndex, adjustedAlpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation);
7070
7370
  }
7371
+ _setDisplaySetIsHydrated(displaySetUID, isHydrated) {
7372
+ const {
7373
+ displaySetService
7374
+ } = this.servicesManager.services;
7375
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
7376
+ if (!displaySet) {
7377
+ return;
7378
+ }
7379
+ displaySet.isHydrated = isHydrated;
7380
+ displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
7381
+ }
7071
7382
  _highlightLabelmap(segmentIndex, alpha, hideOthers, segments, toolGroupId, animationLength, segmentationRepresentation) {
7072
7383
  const newSegmentSpecificConfig = {
7073
7384
  [segmentIndex]: {
@@ -7170,21 +7481,23 @@ class SegmentationService extends src/* PubSubService */.hC {
7170
7481
 
7171
7482
  if (wasActive) {
7172
7483
  const remainingSegmentations = this._getSegmentations();
7173
- if (remainingSegmentations.length) {
7484
+ const remainingHydratedSegmentations = remainingSegmentations.filter(segmentation => segmentation.hydrated);
7485
+ if (remainingHydratedSegmentations.length) {
7174
7486
  const {
7175
7487
  id
7176
- } = remainingSegmentations[0];
7488
+ } = remainingHydratedSegmentations[0];
7177
7489
  this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
7178
7490
  }
7179
7491
  }
7492
+ this._setDisplaySetIsHydrated(segmentationId, false);
7180
7493
  this._broadcastEvent(this.EVENTS.SEGMENTATION_REMOVED, {
7181
7494
  segmentationId
7182
7495
  });
7183
7496
  }
7184
- setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7185
- this._setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
7497
+ setSegmentLabel(segmentationId, segmentIndex, label) {
7498
+ this._setSegmentLabel(segmentationId, segmentIndex, label);
7186
7499
  }
7187
- _setSegmentLabelForSegmentation(segmentationId, segmentIndex, label) {
7500
+ _setSegmentLabel(segmentationId, segmentIndex, label) {
7188
7501
  let suppressEvents = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
7189
7502
  const segmentation = this.getSegmentation(segmentationId);
7190
7503
  if (segmentation === undefined) {
@@ -7202,16 +7515,14 @@ class SegmentationService extends src/* PubSubService */.hC {
7202
7515
  });
7203
7516
  }
7204
7517
  }
7205
- shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
7206
- if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
7518
+ shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segmentationFrameOfReferenceUID) {
7519
+ if (!viewportDisplaySetInstanceUIDs?.length) {
7207
7520
  return false;
7208
7521
  }
7209
7522
  const {
7210
7523
  displaySetService
7211
7524
  } = this.servicesManager.services;
7212
7525
  let shouldDisplaySeg = false;
7213
- const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
7214
- const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
7215
7526
 
7216
7527
  // check if the displaySet is sharing the same frameOfReferenceUID
7217
7528
  // with the new segmentation
@@ -7220,7 +7531,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7220
7531
 
7221
7532
  // Todo: this might not be ideal for use cases such as 4D, since we
7222
7533
  // don't want to show the segmentation for all the frames
7223
- if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segFrameOfReferenceUID) {
7534
+ if (displaySet.isReconstructable && displaySet?.images?.[0]?.FrameOfReferenceUID === segmentationFrameOfReferenceUID) {
7224
7535
  shouldDisplaySeg = true;
7225
7536
  break;
7226
7537
  }
@@ -7374,7 +7685,7 @@ class SegmentationService extends src/* PubSubService */.hC {
7374
7685
  }
7375
7686
  _getSegmentationRepresentation(segmentationId, toolGroupId) {
7376
7687
  const segmentationRepresentations = this.getSegmentationRepresentationsForToolGroup(toolGroupId);
7377
- if (segmentationRepresentations.length === 0) {
7688
+ if (!segmentationRepresentations?.length) {
7378
7689
  return;
7379
7690
  }
7380
7691
 
@@ -7659,7 +7970,8 @@ class CornerstoneCacheService {
7659
7970
  }
7660
7971
  _shouldRenderSegmentation(displaySets) {
7661
7972
  const {
7662
- segmentationService
7973
+ segmentationService,
7974
+ displaySetService
7663
7975
  } = this.servicesManager.services;
7664
7976
  const viewportDisplaySetInstanceUIDs = displaySets.map(_ref2 => {
7665
7977
  let {
@@ -7673,7 +7985,8 @@ class CornerstoneCacheService {
7673
7985
  const segmentations = segmentationService.getSegmentations();
7674
7986
  for (const segmentation of segmentations) {
7675
7987
  const segDisplaySetInstanceUID = segmentation.displaySetInstanceUID;
7676
- const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID);
7988
+ const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
7989
+ const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySet.instances[0].FrameOfReferenceUID);
7677
7990
  if (shouldDisplaySeg) {
7678
7991
  return true;
7679
7992
  }
@@ -7747,16 +8060,17 @@ const DEFAULT_TOOLGROUP_ID = 'default';
7747
8060
  // Return true if the data contains the given display set UID OR the imageId
7748
8061
  // if it is a composite object.
7749
8062
  const dataContains = (data, displaySetUID, imageId) => {
7750
- if (data.displaySetInstanceUID === displaySetUID) return true;
8063
+ if (data.displaySetInstanceUID === displaySetUID) {
8064
+ return true;
8065
+ }
7751
8066
  if (imageId && data.isCompositeStack && data.imageIds) {
7752
8067
  return !!data.imageIds.find(dataId => dataId === imageId);
7753
8068
  }
7754
8069
  return false;
7755
8070
  };
7756
8071
  class ViewportInfo {
7757
- constructor(viewportIndex, viewportId) {
8072
+ constructor(viewportId) {
7758
8073
  this.viewportId = '';
7759
- this.viewportIndex = void 0;
7760
8074
  this.element = void 0;
7761
8075
  this.viewportOptions = void 0;
7762
8076
  this.displaySetOptions = void 0;
@@ -7768,7 +8082,6 @@ class ViewportInfo {
7768
8082
  this.viewportOptions = null;
7769
8083
  this.displaySetOptions = null;
7770
8084
  };
7771
- this.viewportIndex = viewportIndex;
7772
8085
  this.viewportId = viewportId;
7773
8086
  this.setPublicViewportOptions({});
7774
8087
  this.setPublicDisplaySetOptions([{}]);
@@ -7779,7 +8092,9 @@ class ViewportInfo {
7779
8092
  * OR if it is a composite stack and contains the given imageId
7780
8093
  */
7781
8094
  contains(displaySetUID, imageId) {
7782
- if (!this.viewportData?.data) return false;
8095
+ if (!this.viewportData?.data) {
8096
+ return false;
8097
+ }
7783
8098
  if (this.viewportData.data.length) {
7784
8099
  return !!this.viewportData.data.find(data => dataContains(data, displaySetUID, imageId));
7785
8100
  }
@@ -7794,9 +8109,6 @@ class ViewportInfo {
7794
8109
  setViewportId(viewportId) {
7795
8110
  this.viewportId = viewportId;
7796
8111
  }
7797
- setViewportIndex(viewportIndex) {
7798
- this.viewportIndex = viewportIndex;
7799
- }
7800
8112
  setElement(element) {
7801
8113
  this.element = element;
7802
8114
  }
@@ -7806,9 +8118,6 @@ class ViewportInfo {
7806
8118
  getViewportData() {
7807
8119
  return this.viewportData;
7808
8120
  }
7809
- getViewportIndex() {
7810
- return this.viewportIndex;
7811
- }
7812
8121
  getElement() {
7813
8122
  return this.element;
7814
8123
  }
@@ -7819,13 +8128,14 @@ class ViewportInfo {
7819
8128
  // map the displaySetOptions and check if they are undefined then set them to default values
7820
8129
  const displaySetOptions = this.mapDisplaySetOptions(publicDisplaySetOptions);
7821
8130
  this.setDisplaySetOptions(displaySetOptions);
8131
+ return this.displaySetOptions;
7822
8132
  }
7823
8133
  hasDisplaySet(displaySetInstanceUID) {
7824
8134
  // Todo: currently this does not work for non image & referenceImage displaySets.
7825
8135
  // Since SEG and other derived displaySets are loaded in a different way, and not
7826
8136
  // via cornerstoneViewportService
7827
8137
  let viewportData = this.getViewportData();
7828
- if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC) {
8138
+ if (viewportData.viewportType === esm.Enums.ViewportType.ORTHOGRAPHIC || viewportData.viewportType === esm.Enums.ViewportType.VOLUME_3D) {
7829
8139
  viewportData = viewportData;
7830
8140
  return viewportData.data.some(_ref => {
7831
8141
  let {
@@ -7865,6 +8175,7 @@ class ViewportInfo {
7865
8175
  toolGroupId,
7866
8176
  presentationIds
7867
8177
  });
8178
+ return this.viewportOptions;
7868
8179
  }
7869
8180
  setViewportOptions(viewportOptions) {
7870
8181
  this.viewportOptions = viewportOptions;
@@ -7959,7 +8270,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7959
8270
  constructor(servicesManager) {
7960
8271
  super(CornerstoneViewportService_EVENTS);
7961
8272
  this.renderingEngine = void 0;
7962
- this.viewportsInfo = new Map();
7963
8273
  this.viewportsById = new Map();
7964
8274
  this.viewportGridResizeObserver = void 0;
7965
8275
  this.viewportsDisplaySets = new Map();
@@ -7975,37 +8285,16 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
7975
8285
 
7976
8286
  /**
7977
8287
  * Adds the HTML element to the viewportService
7978
- * @param {*} viewportIndex
8288
+ * @param {*} viewportId
7979
8289
  * @param {*} elementRef
7980
8290
  */
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
- }
8291
+ enableViewport(viewportId, elementRef) {
8292
+ const viewportInfo = new Viewport(viewportId);
7996
8293
  viewportInfo.setElement(elementRef);
7997
- this.viewportsInfo.set(viewportIndex, viewportInfo);
7998
8294
  this.viewportsById.set(viewportId, viewportInfo);
7999
8295
  }
8000
8296
  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;
8297
+ return Array.from(this.viewportsById.keys());
8009
8298
  }
8010
8299
 
8011
8300
  /**
@@ -8059,34 +8348,38 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8059
8348
  * created for every new viewport, this will be called whenever the set of
8060
8349
  * viewports is changed, but NOT when the viewport position changes only.
8061
8350
  *
8062
- * @param viewportIndex
8351
+ * @param viewportId - The viewportId to disable
8063
8352
  */
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);
8353
+ disableElement(viewportId) {
8354
+ this.renderingEngine?.disableElement(viewportId);
8355
+
8356
+ // clean up
8073
8357
  this.viewportsById.delete(viewportId);
8358
+ this.viewportsDisplaySets.delete(viewportId);
8074
8359
  }
8075
8360
  setPresentations(viewport, presentations) {
8076
8361
  const properties = presentations?.lutPresentation?.properties;
8077
- if (properties) viewport.setProperties(properties);
8362
+ if (properties) {
8363
+ viewport.setProperties(properties);
8364
+ }
8078
8365
  const camera = presentations?.positionPresentation?.camera;
8079
- if (camera) viewport.setCamera(camera);
8366
+ if (camera) {
8367
+ viewport.setCamera(camera);
8368
+ }
8080
8369
  }
8081
- getPresentation(viewportIndex) {
8082
- const viewportInfo = this.viewportsInfo.get(viewportIndex);
8083
- if (!viewportInfo) return;
8370
+ getPresentation(viewportId) {
8371
+ const viewportInfo = this.viewportsById.get(viewportId);
8372
+ if (!viewportInfo) {
8373
+ return;
8374
+ }
8084
8375
  const {
8085
8376
  viewportType,
8086
8377
  presentationIds
8087
8378
  } = viewportInfo.getViewportOptions();
8088
- const csViewport = this.getCornerstoneViewportByIndex(viewportIndex);
8089
- if (!csViewport) return;
8379
+ const csViewport = this.getCornerstoneViewport(viewportId);
8380
+ if (!csViewport) {
8381
+ return;
8382
+ }
8090
8383
  const properties = csViewport.getProperties();
8091
8384
  if (properties.isComputedVOI) {
8092
8385
  delete properties.voiRange;
@@ -8102,40 +8395,78 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8102
8395
  camera
8103
8396
  };
8104
8397
  }
8398
+ storePresentation(_ref) {
8399
+ let {
8400
+ viewportId
8401
+ } = _ref;
8402
+ const stateSyncService = this.servicesManager.services.stateSyncService;
8403
+ let presentation;
8404
+ try {
8405
+ presentation = this.getPresentation(viewportId);
8406
+ } catch (error) {
8407
+ console.warn(error);
8408
+ }
8409
+ if (!presentation || !presentation.presentationIds) {
8410
+ return;
8411
+ }
8412
+ const {
8413
+ lutPresentationStore,
8414
+ positionPresentationStore
8415
+ } = stateSyncService.getState();
8416
+ const {
8417
+ presentationIds
8418
+ } = presentation;
8419
+ const {
8420
+ lutPresentationId,
8421
+ positionPresentationId
8422
+ } = presentationIds || {};
8423
+ const storeState = {};
8424
+ if (lutPresentationId) {
8425
+ storeState.lutPresentationStore = {
8426
+ ...lutPresentationStore,
8427
+ [lutPresentationId]: presentation
8428
+ };
8429
+ }
8430
+ if (positionPresentationId) {
8431
+ storeState.positionPresentationStore = {
8432
+ ...positionPresentationStore,
8433
+ [positionPresentationId]: presentation
8434
+ };
8435
+ }
8436
+ stateSyncService.store(storeState);
8437
+ }
8105
8438
 
8106
8439
  /**
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
8440
+ * Sets the viewport data for a viewport.
8441
+ * @param viewportId - The ID of the viewport to set the data for.
8442
+ * @param viewportData - The viewport data to set.
8443
+ * @param publicViewportOptions - The public viewport options.
8444
+ * @param publicDisplaySetOptions - The public display set options.
8445
+ * @param presentations - The presentations to set.
8113
8446
  */
8114
- setViewportData(viewportIndex, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8447
+ setViewportData(viewportId, viewportData, publicViewportOptions, publicDisplaySetOptions, presentations) {
8115
8448
  const renderingEngine = this.getRenderingEngine();
8116
- const viewportId = publicViewportOptions.viewportId || this.getViewportId(viewportIndex);
8117
- if (!viewportId) {
8118
- throw new Error('Must define viewportId externally');
8119
- }
8449
+
8450
+ // This is the old viewportInfo, which may have old options but we might be
8451
+ // using its viewport (same viewportId as the new viewportInfo)
8120
8452
  const viewportInfo = this.viewportsById.get(viewportId);
8453
+
8454
+ // We should store the presentation for the current viewport since we can't only
8455
+ // rely to store it WHEN the viewport is disabled since we might keep around the
8456
+ // same viewport/element and just change the viewportData for it (drag and drop etc.)
8457
+ // the disableElement storePresentation handle would not be called in this case
8458
+ // and we would lose the presentation.
8459
+ this.storePresentation({
8460
+ viewportId: viewportInfo.getViewportId()
8461
+ });
8121
8462
  if (!viewportInfo) {
8122
- throw new Error('Viewport info not defined');
8463
+ throw new Error('element is not enabled for the given viewportId');
8123
8464
  }
8124
8465
 
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);
8466
+ // override the viewportOptions and displaySetOptions with the public ones
8467
+ // since those are the newly set ones, we set them here so that it handles defaults
8468
+ const displaySetOptions = viewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
8469
+ const viewportOptions = viewportInfo.setPublicViewportOptions(publicViewportOptions);
8139
8470
  const element = viewportInfo.getElement();
8140
8471
  const type = viewportInfo.getViewportType();
8141
8472
  const background = viewportInfo.getBackground();
@@ -8150,11 +8481,23 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8150
8481
  }
8151
8482
  };
8152
8483
 
8484
+ // Rendering Engine Id set should happen before enabling the element
8485
+ // since there are callbacks that depend on the renderingEngine id
8486
+ // Todo: however, this is a limitation which means that we can't change
8487
+ // the rendering engine id for a given viewport which might be a super edge
8488
+ // case
8489
+ viewportInfo.setRenderingEngineId(renderingEngine.id);
8490
+
8153
8491
  // Todo: this is not optimal at all, we are re-enabling the already enabled
8154
8492
  // element which is not what we want. But enabledElement as part of the
8155
8493
  // renderingEngine is designed to be used like this. This will trigger
8156
8494
  // ENABLED_ELEMENT again and again, which will run onEnableElement callbacks
8157
8495
  renderingEngine.enableElement(viewportInput);
8496
+ viewportInfo.setViewportOptions(viewportOptions);
8497
+ viewportInfo.setDisplaySetOptions(displaySetOptions);
8498
+ viewportInfo.setViewportData(viewportData);
8499
+ viewportInfo.setViewportId(viewportId);
8500
+ this.viewportsById.set(viewportId, viewportInfo);
8158
8501
  const viewport = renderingEngine.getViewport(viewportId);
8159
8502
  this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
8160
8503
 
@@ -8163,7 +8506,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8163
8506
  // invalid data.
8164
8507
  this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
8165
8508
  viewportData,
8166
- viewportIndex,
8167
8509
  viewportId
8168
8510
  });
8169
8511
  }
@@ -8175,31 +8517,8 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8175
8517
  const viewport = this.renderingEngine.getViewport(viewportId);
8176
8518
  return viewport;
8177
8519
  }
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
8520
  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;
8521
+ return this.viewportsById.get(viewportId);
8203
8522
  }
8204
8523
  _setStackViewport(viewport, viewportData, viewportInfo, presentations) {
8205
8524
  const displaySetOptions = viewportInfo.getDisplaySetOptions();
@@ -8236,9 +8555,13 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8236
8555
  }
8237
8556
  }
8238
8557
  viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
8239
- viewport.setProperties(properties);
8558
+ viewport.setProperties({
8559
+ ...properties
8560
+ });
8240
8561
  const camera = presentations.positionPresentation?.camera;
8241
- if (camera) viewport.setCamera(camera);
8562
+ if (camera) {
8563
+ viewport.setCamera(camera);
8564
+ }
8242
8565
  });
8243
8566
  }
8244
8567
  _getInitialImageIndexForViewport(viewportInfo, imageIds) {
@@ -8333,7 +8656,7 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8333
8656
  });
8334
8657
  }
8335
8658
  this.viewportsDisplaySets.set(viewport.id, displaySetInstanceUIDs);
8336
- if (hangingProtocolService.hasCustomImageLoadStrategy() && !hangingProtocolService.customImageLoadPerformed) {
8659
+ if (hangingProtocolService.getShouldPerformCustomImageLoad()) {
8337
8660
  // delegate the volume loading to the hanging protocol service if it has a custom image load strategy
8338
8661
  return hangingProtocolService.runImageLoadStrategy({
8339
8662
  viewportId: viewport.id,
@@ -8395,11 +8718,11 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8395
8718
  };
8396
8719
  });
8397
8720
  await viewport.setVolumes(volumeInputArray);
8398
- volumesProperties.forEach(_ref => {
8721
+ volumesProperties.forEach(_ref2 => {
8399
8722
  let {
8400
8723
  properties,
8401
8724
  volumeId
8402
- } = _ref;
8725
+ } = _ref2;
8403
8726
  viewport.setProperties(properties, volumeId);
8404
8727
  });
8405
8728
  this.setPresentations(viewport, presentations);
@@ -8448,13 +8771,25 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8448
8771
  continue;
8449
8772
  }
8450
8773
 
8451
- // otherwise, check if the hydrated segmentations are in the same FOR
8774
+ // otherwise, check if the hydrated segmentations are in the same FrameOfReferenceUID
8452
8775
  // as the primary displaySet, if so add the representation (since it was not there)
8453
8776
  const {
8454
- id: segDisplaySetInstanceUID,
8455
- type
8777
+ id: segDisplaySetInstanceUID
8456
8778
  } = segmentation;
8457
- const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8779
+ let segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
8780
+ if (!segFrameOfReferenceUID) {
8781
+ // if the segmentation displaySet does not have a FrameOfReferenceUID, we might check the
8782
+ // segmentation itself maybe it has a FrameOfReferenceUID
8783
+ const {
8784
+ FrameOfReferenceUID
8785
+ } = segmentation;
8786
+ if (FrameOfReferenceUID) {
8787
+ segFrameOfReferenceUID = FrameOfReferenceUID;
8788
+ }
8789
+ }
8790
+ if (!segFrameOfReferenceUID) {
8791
+ return;
8792
+ }
8458
8793
  let shouldDisplaySeg = false;
8459
8794
  for (const displaySetInstanceUID of displaySetInstanceUIDs) {
8460
8795
  const primaryFrameOfReferenceUID = this._getFrameOfReferenceUID(displaySetInstanceUID);
@@ -8487,13 +8822,12 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8487
8822
 
8488
8823
  // Todo: keepCamera is an interim solution until we have a better solution for
8489
8824
  // keeping the camera position when the viewport data is changed
8490
- updateViewport(viewportIndex, viewportData) {
8825
+ updateViewport(viewportId, viewportData) {
8491
8826
  let keepCamera = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8492
- const viewportInfo = this.getViewportInfoByIndex(viewportIndex);
8493
- const viewportId = viewportInfo.getViewportId();
8827
+ const viewportInfo = this.getViewportInfo(viewportId);
8494
8828
  const viewport = this.getCornerstoneViewport(viewportId);
8495
8829
  const viewportCamera = viewport.getCamera();
8496
- if (viewport instanceof esm.VolumeViewport) {
8830
+ if (viewport instanceof esm.VolumeViewport || viewport instanceof esm.VolumeViewport3D) {
8497
8831
  this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
8498
8832
  if (keepCamera) {
8499
8833
  viewport.setCamera(viewportCamera);
@@ -8548,23 +8882,6 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8548
8882
  return slabThickness;
8549
8883
  }
8550
8884
  }
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
8885
  _getFrameOfReferenceUID(displaySetInstanceUID) {
8569
8886
  const {
8570
8887
  displaySetService
@@ -8602,28 +8919,28 @@ class CornerstoneViewportService extends src/* PubSubService */.hC {
8602
8919
  *
8603
8920
  * @param measurement
8604
8921
  * The measurement that is desired to view.
8605
- * @param activeViewportIndex - the index that was active at the time the jump
8922
+ * @param activeViewportId - the index that was active at the time the jump
8606
8923
  * was initiated.
8607
- * @return the viewportIndex to display the given measurement
8924
+ * @return the viewportId that the measurement should be displayed in.
8608
8925
  */
8609
- getViewportIndexToJump(activeViewportIndex, displaySetInstanceUID, cameraProps) {
8610
- const viewportInfo = this.viewportsInfo.get(activeViewportIndex);
8926
+ getViewportIdToJump(activeViewportId, displaySetInstanceUID, cameraProps) {
8927
+ const viewportInfo = this.getViewportInfo(activeViewportId);
8611
8928
  const {
8612
8929
  referencedImageId
8613
8930
  } = cameraProps;
8614
8931
  if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
8615
- return activeViewportIndex;
8932
+ return activeViewportId;
8616
8933
  }
8617
- return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportIndex ?? -1;
8934
+ return [...this.viewportsById.values()].find(viewportInfo => viewportInfo.contains(displaySetInstanceUID, referencedImageId))?.viewportId ?? null;
8618
8935
  }
8619
8936
  }
8620
8937
  CornerstoneViewportService.REGISTRATION = {
8621
8938
  name: 'cornerstoneViewportService',
8622
8939
  altName: 'CornerstoneViewportService',
8623
- create: _ref2 => {
8940
+ create: _ref3 => {
8624
8941
  let {
8625
8942
  servicesManager
8626
- } = _ref2;
8943
+ } = _ref3;
8627
8944
  return new CornerstoneViewportService(servicesManager);
8628
8945
  }
8629
8946
  };
@@ -8632,7 +8949,7 @@ CornerstoneViewportService.REGISTRATION = {
8632
8949
 
8633
8950
 
8634
8951
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
8635
- var dicomweb_client_es = __webpack_require__(75935);
8952
+ var dicomweb_client_es = __webpack_require__(97604);
8636
8953
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/dicomLoaderService.js
8637
8954
 
8638
8955
 
@@ -8707,7 +9024,7 @@ class DicomLoaderService {
8707
9024
  // Use referenced imageInstance
8708
9025
  const imageInstance = getImageInstance(dataset);
8709
9026
  const nonImageInstance = getNonImageInstance(dataset);
8710
- if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId.startsWith('dicomfile')) {
9027
+ if (!imageInstance && !nonImageInstance || !nonImageInstance.imageId?.startsWith('dicomfile')) {
8711
9028
  return;
8712
9029
  }
8713
9030
  const instance = imageInstance || nonImageInstance;
@@ -8824,7 +9141,6 @@ function getHandlesFromPoints(points) {
8824
9141
 
8825
9142
 
8826
9143
 
8827
-
8828
9144
  ;// CONCATENATED MODULE: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/index.ts
8829
9145
 
8830
9146
 
@@ -8849,11 +9165,10 @@ function src_extends() { src_extends = Object.assign ? Object.assign.bind() : fu
8849
9165
 
8850
9166
 
8851
9167
 
8852
-
8853
9168
 
8854
9169
 
8855
9170
  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));
9171
+ return Promise.all(/* import() */[__webpack_require__.e(23), __webpack_require__.e(181)]).then(__webpack_require__.bind(__webpack_require__, 86181));
8857
9172
  });
8858
9173
  const OHIFCornerstoneViewport = props => {
8859
9174
  return /*#__PURE__*/react.createElement(react.Suspense, {
@@ -8876,7 +9191,6 @@ const cornerstoneExtension = {
8876
9191
  esm.imageLoadPoolManager.clearRequestStack(type);
8877
9192
  esm.imageRetrievalPoolManager.clearRequestStack(type);
8878
9193
  });
8879
- destroy();
8880
9194
  (0,state/* reset */.mc)();
8881
9195
  },
8882
9196
  /**
@@ -8956,7 +9270,7 @@ const cornerstoneExtension = {
8956
9270
 
8957
9271
  /***/ }),
8958
9272
 
8959
- /***/ 21922:
9273
+ /***/ 73704:
8960
9274
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8961
9275
 
8962
9276
  "use strict";
@@ -8976,21 +9290,21 @@ const state = {
8976
9290
  * @param {HTMLElement} dom Active viewport element.
8977
9291
  * @return void
8978
9292
  */
8979
- const setEnabledElement = (viewportIndex, element, context) => {
9293
+ const setEnabledElement = (viewportId, element, context) => {
8980
9294
  const targetContext = context || state.DEFAULT_CONTEXT;
8981
- state.enabledElements[viewportIndex] = {
9295
+ state.enabledElements[viewportId] = {
8982
9296
  element,
8983
9297
  context: targetContext
8984
9298
  };
8985
9299
  };
8986
9300
 
8987
9301
  /**
8988
- * Grabs the enabled element `dom` reference of an ative viewport.
9302
+ * Grabs the enabled element `dom` reference of an active viewport.
8989
9303
  *
8990
9304
  * @return {HTMLElement} Active viewport element.
8991
9305
  */
8992
- const getEnabledElement = viewportIndex => {
8993
- return state.enabledElements[viewportIndex];
9306
+ const getEnabledElement = viewportId => {
9307
+ return state.enabledElements[viewportId];
8994
9308
  };
8995
9309
  const reset = () => {
8996
9310
  state.enabledElements = {};
@@ -8999,14 +9313,14 @@ const reset = () => {
8999
9313
 
9000
9314
  /***/ }),
9001
9315
 
9002
- /***/ 63130:
9316
+ /***/ 87172:
9003
9317
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9004
9318
 
9005
9319
  "use strict";
9006
9320
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9007
9321
  /* harmony export */ Z: () => (/* binding */ getSOPInstanceAttributes)
9008
9322
  /* harmony export */ });
9009
- /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77331);
9323
+ /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3743);
9010
9324
 
9011
9325
 
9012
9326
  /**