@ohif/app 3.0.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/151.bundle.07bac9172580a60fae7a.js +2579 -0
  2. package/dist/192.bundle.62be5f0ef9705a485071.js +894 -0
  3. package/dist/199.bundle.2286f24cf0a068e7f50c.js +480 -0
  4. package/dist/205.bundle.39e6c847d618ad2b1b7a.js +62 -0
  5. package/dist/208.bundle.23748a85dfdc79c05d3a.js +864 -0
  6. package/dist/270.bundle.abbdb5348274bae3e8bc.js +23906 -0
  7. package/dist/283.bundle.33f99a75a5e2d9333da2.js +2939 -0
  8. package/dist/295.bundle.5105ce962be15c92484d.js +48 -0
  9. package/dist/331.bundle.7ac7b142d249d14fd99e.js +73034 -0
  10. package/dist/351.bundle.c5d7279ef42e30f61e08.js +1471 -0
  11. package/dist/351.css +3 -0
  12. package/dist/36785fbd89b0e17f6099.wasm +0 -0
  13. package/dist/381.bundle.0905e683605fcbc0895f.js +1009 -0
  14. package/dist/404.bundle.0f7a500421f246153d89.js +706 -0
  15. package/dist/50.bundle.4cb103cd20f5ffccf927.js +324 -0
  16. package/dist/5004fdc02f329ce53b69.wasm +0 -0
  17. package/dist/531.bundle.1bc152c87c7e2e987d2b.js +5935 -0
  18. package/dist/55.bundle.a5a215e13a8511f7aee7.js +685 -0
  19. package/dist/55.css +3 -0
  20. package/dist/569.bundle.d147c0aa0604f8ea2094.js +514 -0
  21. package/dist/581.bundle.646c89c5c3e3ee096363.js +508 -0
  22. package/dist/606.bundle.5d876f5f3dd8287f0a28.js +4939 -0
  23. package/dist/610.min.worker.js +2 -0
  24. package/dist/610.min.worker.js.map +1 -0
  25. package/dist/616.bundle.bec4736d8c9513e62856.js +686 -0
  26. package/dist/62ab5d58a2bea7b5a1dc.wasm +0 -0
  27. package/dist/642.bundle.030d908e22c8ff5611f3.js +169 -0
  28. package/dist/65916ef3def695744bda.wasm +0 -0
  29. package/dist/664.bundle.4792c88ae0d6d4b5ed13.js +901 -0
  30. package/dist/707.bundle.0a74aa3e61ed002eb3c6.js +9049 -0
  31. package/dist/707.css +1 -0
  32. package/dist/728.bundle.d13856835357400fef82.js +26221 -0
  33. package/dist/744.bundle.53b07e48e07a11e920ac.js +2355 -0
  34. package/dist/75788f12450d4c5ed494.wasm +0 -0
  35. package/dist/75a0c2dfe07b824c7d21.wasm +0 -0
  36. package/dist/780.bundle.f60ac1906e0ae080dee8.js +4769 -0
  37. package/dist/790.bundle.b4df2c5d78a2a565b150.js +454 -0
  38. package/dist/799.bundle.3fff638815e355b0bdfd.js +271 -0
  39. package/dist/806.css +1 -0
  40. package/dist/82.bundle.a24015533196e05d190e.js +6104 -0
  41. package/dist/917.bundle.a094ae9e9de6df4119ae.js +196 -0
  42. package/dist/926.bundle.dbc9d0e591cb9217fda2.js +72552 -0
  43. package/dist/935.bundle.deeffff0e4f7b528e3c3.js +1849 -0
  44. package/dist/945.min.worker.js +2 -0
  45. package/dist/945.min.worker.js.map +1 -0
  46. package/dist/953.bundle.c14d9eb6400f697019ee.js +449 -0
  47. package/dist/973.bundle.4100cf103686b64938d1.js +261 -0
  48. package/dist/976.bundle.2720eb892514e1818018.js +2725 -0
  49. package/dist/984.bundle.157fc66ea5040e1364af.js +1842 -0
  50. package/dist/_headers +6 -0
  51. package/dist/_redirects +6 -0
  52. package/dist/app-config.js +215 -0
  53. package/dist/app.bundle.253eeb2a7ee986e89c50.js +154621 -0
  54. package/dist/app.bundle.css +21 -0
  55. package/dist/assets/android-chrome-144x144.png +0 -0
  56. package/dist/assets/android-chrome-192x192.png +0 -0
  57. package/dist/assets/android-chrome-256x256.png +0 -0
  58. package/dist/assets/android-chrome-36x36.png +0 -0
  59. package/dist/assets/android-chrome-384x384.png +0 -0
  60. package/dist/assets/android-chrome-48x48.png +0 -0
  61. package/dist/assets/android-chrome-512x512.png +0 -0
  62. package/dist/assets/android-chrome-72x72.png +0 -0
  63. package/dist/assets/android-chrome-96x96.png +0 -0
  64. package/dist/assets/apple-touch-icon-1024x1024.png +0 -0
  65. package/dist/assets/apple-touch-icon-114x114.png +0 -0
  66. package/dist/assets/apple-touch-icon-120x120.png +0 -0
  67. package/dist/assets/apple-touch-icon-144x144.png +0 -0
  68. package/dist/assets/apple-touch-icon-152x152.png +0 -0
  69. package/dist/assets/apple-touch-icon-167x167.png +0 -0
  70. package/dist/assets/apple-touch-icon-180x180.png +0 -0
  71. package/dist/assets/apple-touch-icon-57x57.png +0 -0
  72. package/dist/assets/apple-touch-icon-60x60.png +0 -0
  73. package/dist/assets/apple-touch-icon-72x72.png +0 -0
  74. package/dist/assets/apple-touch-icon-76x76.png +0 -0
  75. package/dist/assets/apple-touch-icon-precomposed.png +0 -0
  76. package/dist/assets/apple-touch-icon.png +0 -0
  77. package/dist/assets/apple-touch-startup-image-1182x2208.png +0 -0
  78. package/dist/assets/apple-touch-startup-image-1242x2148.png +0 -0
  79. package/dist/assets/apple-touch-startup-image-1496x2048.png +0 -0
  80. package/dist/assets/apple-touch-startup-image-1536x2008.png +0 -0
  81. package/dist/assets/apple-touch-startup-image-320x460.png +0 -0
  82. package/dist/assets/apple-touch-startup-image-640x1096.png +0 -0
  83. package/dist/assets/apple-touch-startup-image-640x920.png +0 -0
  84. package/dist/assets/apple-touch-startup-image-748x1024.png +0 -0
  85. package/dist/assets/apple-touch-startup-image-750x1294.png +0 -0
  86. package/dist/assets/apple-touch-startup-image-768x1004.png +0 -0
  87. package/dist/assets/browserconfig.xml +12 -0
  88. package/dist/assets/coast-228x228.png +0 -0
  89. package/dist/assets/favicon-16x16.png +0 -0
  90. package/dist/assets/favicon-32x32.png +0 -0
  91. package/dist/assets/favicon.ico +0 -0
  92. package/dist/assets/firefox_app_128x128.png +0 -0
  93. package/dist/assets/firefox_app_512x512.png +0 -0
  94. package/dist/assets/firefox_app_60x60.png +0 -0
  95. package/dist/assets/manifest.webapp +14 -0
  96. package/dist/assets/mstile-144x144.png +0 -0
  97. package/dist/assets/mstile-150x150.png +0 -0
  98. package/dist/assets/mstile-310x150.png +0 -0
  99. package/dist/assets/mstile-310x310.png +0 -0
  100. package/dist/assets/mstile-70x70.png +0 -0
  101. package/dist/assets/yandex-browser-50x50.png +0 -0
  102. package/dist/assets/yandex-browser-manifest.json +9 -0
  103. package/dist/b6b803111e2d06a825bd.wasm +0 -0
  104. package/dist/c22b37c3488e1d6c3aa4.wasm +0 -0
  105. package/dist/cornerstoneDICOMImageLoader.min.js +2 -0
  106. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -0
  107. package/dist/dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js +12 -0
  108. package/dist/dicomMicroscopyViewer.min.js +3 -0
  109. package/dist/dicomMicroscopyViewer.min.js.LICENSE.txt +29 -0
  110. package/dist/es6-shim.min.js +12 -0
  111. package/dist/google.js +75 -0
  112. package/dist/index.html +1 -0
  113. package/dist/index.worker.ea71efba2ce63c499055.worker.js +2 -0
  114. package/dist/index.worker.ea71efba2ce63c499055.worker.js.map +1 -0
  115. package/dist/index.worker.min.worker.js +2 -0
  116. package/dist/index.worker.min.worker.js.map +1 -0
  117. package/dist/init-service-worker.js +59 -0
  118. package/dist/manifest.json +59 -0
  119. package/dist/ohif-logo-light.svg +15 -0
  120. package/dist/ohif-logo.svg +15 -0
  121. package/dist/oidc-client.min.js +46 -0
  122. package/dist/polyfill.min.js +1 -0
  123. package/dist/silent-refresh.html +16 -0
  124. package/dist/sw.js +56 -0
  125. package/package.json +24 -23
@@ -0,0 +1,1842 @@
1
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[984],{
2
+
3
+ /***/ 65948:
4
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5
+
6
+ "use strict";
7
+ // ESM COMPAT FLAG
8
+ __webpack_require__.r(__webpack_exports__);
9
+
10
+ // EXPORTS
11
+ __webpack_require__.d(__webpack_exports__, {
12
+ "createReferencedImageDisplaySet": () => (/* reexport */ utils_createReferencedImageDisplaySet),
13
+ "default": () => (/* binding */ cornerstone_dicom_sr_src),
14
+ "hydrateStructuredReport": () => (/* reexport */ hydrateStructuredReport/* default */.Z),
15
+ "srProtocol": () => (/* reexport */ srProtocol)
16
+ });
17
+
18
+ // EXTERNAL MODULE: ../../../node_modules/react/index.js
19
+ var react = __webpack_require__(32735);
20
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/package.json
21
+ const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-cornerstone-dicom-sr"}');
22
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/id.js
23
+
24
+ const id = package_namespaceObject.u2;
25
+ const SOPClassHandlerName = 'dicom-sr';
26
+ const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
27
+
28
+ // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
29
+ var src = __webpack_require__(48501);
30
+ // EXTERNAL MODULE: ../../../node_modules/gl-matrix/esm/index.js + 10 modules
31
+ var esm = __webpack_require__(88256);
32
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 321 modules
33
+ var dist_esm = __webpack_require__(57270);
34
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 335 modules
35
+ var core_dist_esm = __webpack_require__(77331);
36
+ // EXTERNAL MODULE: ../../../extensions/cornerstone-dicom-sr/src/tools/modules/dicomSRModule.js
37
+ var dicomSRModule = __webpack_require__(94709);
38
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/constants/scoordTypes.js
39
+ /* harmony default export */ const scoordTypes = ({
40
+ POINT: 'POINT',
41
+ MULTIPOINT: 'MULTIPOINT',
42
+ POLYLINE: 'POLYLINE',
43
+ CIRCLE: 'CIRCLE',
44
+ ELLIPSE: 'ELLIPSE'
45
+ });
46
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/tools/DICOMSRDisplayTool.ts
47
+
48
+
49
+
50
+
51
+ class DICOMSRDisplayTool extends dist_esm.AnnotationTool {
52
+ constructor() {
53
+ let toolProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
54
+ let defaultToolProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
55
+ configuration: {}
56
+ };
57
+ super(toolProps, defaultToolProps);
58
+ // This tool should not inherit from AnnotationTool and we should not need
59
+ // to add the following lines.
60
+ this.isPointNearTool = () => null;
61
+ this.getHandleNearImagePoint = () => null;
62
+ this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
63
+ const {
64
+ viewport
65
+ } = enabledElement;
66
+ const {
67
+ element
68
+ } = viewport;
69
+ let annotations = dist_esm.annotation.state.getAnnotations(this.getToolName(), element);
70
+
71
+ // Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
72
+ if (!annotations?.length) {
73
+ return;
74
+ }
75
+ annotations = this.filterInteractableAnnotationsForElement(element, annotations);
76
+ if (!annotations?.length) {
77
+ return;
78
+ }
79
+ const trackingUniqueIdentifiersForElement = (0,dicomSRModule/* getTrackingUniqueIdentifiersForElement */.yR)(element);
80
+ const {
81
+ activeIndex,
82
+ trackingUniqueIdentifiers
83
+ } = trackingUniqueIdentifiersForElement;
84
+ const activeTrackingUniqueIdentifier = trackingUniqueIdentifiers[activeIndex];
85
+
86
+ // Filter toolData to only render the data for the active SR.
87
+ const filteredAnnotations = annotations.filter(annotation => trackingUniqueIdentifiers.includes(annotation.data?.cachedStats?.TrackingUniqueIdentifier));
88
+ if (!viewport._actors?.size) {
89
+ return;
90
+ }
91
+ const styleSpecifier = {
92
+ toolGroupId: this.toolGroupId,
93
+ toolName: this.getToolName(),
94
+ viewportId: enabledElement.viewport.id
95
+ };
96
+ for (let i = 0; i < filteredAnnotations.length; i++) {
97
+ const annotation = filteredAnnotations[i];
98
+ const annotationUID = annotation.annotationUID;
99
+ const {
100
+ renderableData
101
+ } = annotation.data.cachedStats;
102
+ const {
103
+ cachedStats
104
+ } = annotation.data;
105
+ const {
106
+ referencedImageId
107
+ } = annotation.metadata;
108
+ styleSpecifier.annotationUID = annotationUID;
109
+ const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
110
+ const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
111
+ const color = cachedStats.TrackingUniqueIdentifier === activeTrackingUniqueIdentifier ? 'rgb(0, 255, 0)' : this.getStyle('color', styleSpecifier, annotation);
112
+ const options = {
113
+ color,
114
+ lineDash,
115
+ lineWidth
116
+ };
117
+ Object.keys(renderableData).forEach(GraphicType => {
118
+ const renderableDataForGraphicType = renderableData[GraphicType];
119
+ let renderMethod;
120
+ let canvasCoordinatesAdapter;
121
+ switch (GraphicType) {
122
+ case scoordTypes.POINT:
123
+ renderMethod = this.renderPoint;
124
+ break;
125
+ case scoordTypes.MULTIPOINT:
126
+ renderMethod = this.renderMultipoint;
127
+ break;
128
+ case scoordTypes.POLYLINE:
129
+ renderMethod = this.renderPolyLine;
130
+ break;
131
+ case scoordTypes.CIRCLE:
132
+ renderMethod = this.renderEllipse;
133
+ break;
134
+ case scoordTypes.ELLIPSE:
135
+ renderMethod = this.renderEllipse;
136
+ canvasCoordinatesAdapter = dist_esm.utilities.math.ellipse.getCanvasEllipseCorners;
137
+ break;
138
+ default:
139
+ throw new Error(`Unsupported GraphicType: ${GraphicType}`);
140
+ }
141
+ const canvasCoordinates = renderMethod(svgDrawingHelper, viewport, renderableDataForGraphicType, annotationUID, referencedImageId, options);
142
+ this.renderTextBox(svgDrawingHelper, viewport, canvasCoordinates, canvasCoordinatesAdapter, annotation, styleSpecifier, options);
143
+ });
144
+ }
145
+ };
146
+ }
147
+ _getTextBoxLinesFromLabels(labels) {
148
+ // TODO -> max 3 for now (label + shortAxis + longAxis), need a generic solution for this!
149
+
150
+ const labelLength = Math.min(labels.length, 3);
151
+ const lines = [];
152
+ for (let i = 0; i < labelLength; i++) {
153
+ const labelEntry = labels[i];
154
+ lines.push(`${_labelToShorthand(labelEntry.label)}${labelEntry.value}`);
155
+ }
156
+ return lines;
157
+ }
158
+ renderPolyLine(svgDrawingHelper, viewport, renderableData, annotationUID, referencedImageId, options) {
159
+ const drawingOptions = {
160
+ color: options.color,
161
+ width: options.lineWidth
162
+ };
163
+ let allCanvasCoordinates = [];
164
+ renderableData.map((data, index) => {
165
+ const canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
166
+ const lineUID = `${index}`;
167
+ if (canvasCoordinates.length === 2) {
168
+ dist_esm.drawing.drawLine(svgDrawingHelper, annotationUID, lineUID, canvasCoordinates[0], canvasCoordinates[1], drawingOptions);
169
+ } else {
170
+ dist_esm.drawing.drawPolyline(svgDrawingHelper, annotationUID, lineUID, canvasCoordinates, drawingOptions);
171
+ }
172
+ allCanvasCoordinates = allCanvasCoordinates.concat(canvasCoordinates);
173
+ });
174
+ return allCanvasCoordinates; // used for drawing textBox
175
+ }
176
+
177
+ renderMultipoint(svgDrawingHelper, viewport, renderableData, annotationUID, referencedImageId, options) {
178
+ let canvasCoordinates;
179
+ renderableData.map((data, index) => {
180
+ canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
181
+ const handleGroupUID = '0';
182
+ dist_esm.drawing.drawHandles(svgDrawingHelper, annotationUID, handleGroupUID, canvasCoordinates, {
183
+ color: options.color
184
+ });
185
+ });
186
+ }
187
+ renderPoint(svgDrawingHelper, viewport, renderableData, annotationUID, referencedImageId, options) {
188
+ const canvasCoordinates = [];
189
+ renderableData.map((data, index) => {
190
+ const point = data[0];
191
+ // This gives us one point for arrow
192
+ canvasCoordinates.push(viewport.worldToCanvas(point));
193
+
194
+ // We get the other point for the arrow by using the image size
195
+ const imagePixelModule = core_dist_esm.metaData.get('imagePixelModule', referencedImageId);
196
+ let xOffset = 10;
197
+ let yOffset = 10;
198
+ if (imagePixelModule) {
199
+ const {
200
+ columns,
201
+ rows
202
+ } = imagePixelModule;
203
+ xOffset = columns / 10;
204
+ yOffset = rows / 10;
205
+ }
206
+ const imagePoint = core_dist_esm.utilities.worldToImageCoords(referencedImageId, point);
207
+ const arrowEnd = core_dist_esm.utilities.imageToWorldCoords(referencedImageId, [imagePoint[0] + xOffset, imagePoint[1] + yOffset]);
208
+ canvasCoordinates.push(viewport.worldToCanvas(arrowEnd));
209
+ const arrowUID = `${index}`;
210
+
211
+ // Todo: handle drawing probe as probe, currently we are drawing it as an arrow
212
+ dist_esm.drawing.drawArrow(svgDrawingHelper, annotationUID, arrowUID, canvasCoordinates[1], canvasCoordinates[0], {
213
+ color: options.color,
214
+ width: options.lineWidth
215
+ });
216
+ });
217
+ return canvasCoordinates; // used for drawing textBox
218
+ }
219
+
220
+ renderEllipse(svgDrawingHelper, viewport, renderableData, annotationUID, referencedImageId, options) {
221
+ let canvasCoordinates;
222
+ renderableData.map((data, index) => {
223
+ if (data.length === 0) {
224
+ // since oblique ellipse is not supported for hydration right now
225
+ // we just return
226
+ return;
227
+ }
228
+ const ellipsePointsWorld = data;
229
+ const rotation = viewport.getRotation();
230
+ canvasCoordinates = ellipsePointsWorld.map(p => viewport.worldToCanvas(p));
231
+ let canvasCorners;
232
+ if (rotation == 90 || rotation == 270) {
233
+ canvasCorners = dist_esm.utilities.math.ellipse.getCanvasEllipseCorners([canvasCoordinates[2], canvasCoordinates[3], canvasCoordinates[0], canvasCoordinates[1]]);
234
+ } else {
235
+ canvasCorners = dist_esm.utilities.math.ellipse.getCanvasEllipseCorners(canvasCoordinates);
236
+ }
237
+ const lineUID = `${index}`;
238
+ dist_esm.drawing.drawEllipse(svgDrawingHelper, annotationUID, lineUID, canvasCorners[0], canvasCorners[1], {
239
+ color: options.color,
240
+ width: options.lineWidth
241
+ });
242
+ });
243
+ return canvasCoordinates;
244
+ }
245
+ renderTextBox(svgDrawingHelper, viewport, canvasCoordinates, canvasCoordinatesAdapter, annotation, styleSpecifier) {
246
+ let options = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
247
+ if (!canvasCoordinates || !annotation) {
248
+ return;
249
+ }
250
+ const {
251
+ annotationUID,
252
+ data = {}
253
+ } = annotation;
254
+ const {
255
+ label
256
+ } = data;
257
+ const {
258
+ color
259
+ } = options;
260
+ let adaptedCanvasCoordinates = canvasCoordinates;
261
+ // adapt coordinates if there is an adapter
262
+ if (typeof canvasCoordinatesAdapter === 'function') {
263
+ adaptedCanvasCoordinates = canvasCoordinatesAdapter(canvasCoordinates);
264
+ }
265
+ const textLines = this._getTextBoxLinesFromLabels(label);
266
+ const canvasTextBoxCoords = dist_esm.utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
267
+ annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
268
+ const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
269
+ const textBoxUID = '1';
270
+ const textBoxOptions = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
271
+ const boundingBox = dist_esm.drawing.drawLinkedTextBox(svgDrawingHelper, annotationUID, textBoxUID, textLines, textBoxPosition, canvasCoordinates, {}, {
272
+ ...textBoxOptions,
273
+ color
274
+ });
275
+ const {
276
+ x: left,
277
+ y: top,
278
+ width,
279
+ height
280
+ } = boundingBox;
281
+ annotation.data.handles.textBox.worldBoundingBox = {
282
+ topLeft: viewport.canvasToWorld([left, top]),
283
+ topRight: viewport.canvasToWorld([left + width, top]),
284
+ bottomLeft: viewport.canvasToWorld([left, top + height]),
285
+ bottomRight: viewport.canvasToWorld([left + width, top + height])
286
+ };
287
+ }
288
+ }
289
+ DICOMSRDisplayTool.toolName = 'DICOMSRDisplay';
290
+ const SHORT_HAND_MAP = {
291
+ 'Short Axis': 'W: ',
292
+ 'Long Axis': 'L: ',
293
+ AREA: 'Area: ',
294
+ Length: '',
295
+ CORNERSTONEFREETEXT: ''
296
+ };
297
+ function _labelToShorthand(label) {
298
+ const shortHand = SHORT_HAND_MAP[label];
299
+ if (shortHand !== undefined) {
300
+ return shortHand;
301
+ }
302
+ return label;
303
+ }
304
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/tools/toolNames.ts
305
+
306
+ const toolNames = {
307
+ DICOMSRDisplay: DICOMSRDisplayTool.toolName,
308
+ SRLength: 'SRLength',
309
+ SRBidirectional: 'SRBidirectional',
310
+ SREllipticalROI: 'SREllipticalROI',
311
+ SRCircleROI: 'SRCircleROI',
312
+ SRArrowAnnotate: 'SRArrowAnnotate',
313
+ SRAngle: 'SRAngle',
314
+ SRCobbAngle: 'SRCobbAngle',
315
+ SRRectangleROI: 'SRRectangleROI',
316
+ SRPlanarFreehandROI: 'SRPlanarFreehandROI'
317
+ };
318
+ /* harmony default export */ const tools_toolNames = (toolNames);
319
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/addMeasurement.ts
320
+
321
+
322
+
323
+
324
+
325
+ const EPSILON = 1e-4;
326
+ const supportedLegacyCornerstoneTags = (/* unused pure expression or super */ null && (['cornerstoneTools@^4.0.0']));
327
+ function addMeasurement(measurement, imageId, displaySetInstanceUID) {
328
+ // TODO -> Render rotated ellipse .
329
+ const toolName = tools_toolNames.DICOMSRDisplay;
330
+ const measurementData = {
331
+ TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier,
332
+ renderableData: {},
333
+ labels: measurement.labels,
334
+ imageId
335
+ };
336
+ measurement.coords.forEach(coord => {
337
+ const {
338
+ GraphicType,
339
+ GraphicData
340
+ } = coord;
341
+ if (measurementData.renderableData[GraphicType] === undefined) {
342
+ measurementData.renderableData[GraphicType] = [];
343
+ }
344
+ measurementData.renderableData[GraphicType].push(_getRenderableData(GraphicType, GraphicData, imageId, measurement.TrackingIdentifier));
345
+ });
346
+
347
+ // Use the metadata provider to grab its imagePlaneModule metadata
348
+ const imagePlaneModule = core_dist_esm.metaData.get('imagePlaneModule', imageId);
349
+ const annotationManager = dist_esm.annotation.state.getAnnotationManager();
350
+
351
+ // Create Cornerstone3D Annotation from measurement
352
+ const frameNumber = measurement.coords[0].ReferencedSOPSequence && measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber || 1;
353
+ const SRAnnotation = {
354
+ annotationUID: measurement.TrackingUniqueIdentifier,
355
+ metadata: {
356
+ FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID,
357
+ toolName: toolName,
358
+ referencedImageId: imageId
359
+ },
360
+ data: {
361
+ label: measurement.labels,
362
+ handles: {
363
+ textBox: {}
364
+ },
365
+ cachedStats: {
366
+ TrackingUniqueIdentifier: measurementData.TrackingUniqueIdentifier,
367
+ renderableData: measurementData.renderableData
368
+ },
369
+ frameNumber: frameNumber
370
+ }
371
+ };
372
+ annotationManager.addAnnotation(SRAnnotation);
373
+ measurement.loaded = true;
374
+ measurement.imageId = imageId;
375
+ measurement.displaySetInstanceUID = displaySetInstanceUID;
376
+
377
+ // Remove the unneeded coord now its processed, but keep the SOPInstanceUID.
378
+ // NOTE: We assume that each SCOORD in the MeasurementGroup maps onto one frame,
379
+ // It'd be super weird if it didn't anyway as a SCOORD.
380
+ measurement.ReferencedSOPInstanceUID = measurement.coords[0].ReferencedSOPSequence.ReferencedSOPInstanceUID;
381
+ measurement.frameNumber = frameNumber;
382
+ delete measurement.coords;
383
+ }
384
+ function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifier) {
385
+ const [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
386
+ let renderableData;
387
+ switch (GraphicType) {
388
+ case scoordTypes.POINT:
389
+ case scoordTypes.MULTIPOINT:
390
+ case scoordTypes.POLYLINE:
391
+ renderableData = [];
392
+ for (let i = 0; i < GraphicData.length; i += 2) {
393
+ const worldPos = core_dist_esm.utilities.imageToWorldCoords(imageId, [GraphicData[i], GraphicData[i + 1]]);
394
+ renderableData.push(worldPos);
395
+ }
396
+ break;
397
+ case scoordTypes.CIRCLE:
398
+ {
399
+ const pointsWorld = [];
400
+ for (let i = 0; i < GraphicData.length; i += 2) {
401
+ const worldPos = core_dist_esm.utilities.imageToWorldCoords(imageId, [GraphicData[i], GraphicData[i + 1]]);
402
+ pointsWorld.push(worldPos);
403
+ }
404
+
405
+ // We do not have an explicit draw circle svg helper in Cornerstone3D at
406
+ // this time, but we can use the ellipse svg helper to draw a circle, so
407
+ // here we reshape the data for that purpose.
408
+ const center = pointsWorld[0];
409
+ const onPerimeter = pointsWorld[1];
410
+ const radius = esm/* vec3.distance */.R3.distance(center, onPerimeter);
411
+ const imagePlaneModule = core_dist_esm.metaData.get('imagePlaneModule', imageId);
412
+ if (!imagePlaneModule) {
413
+ throw new Error('No imagePlaneModule found');
414
+ }
415
+ const {
416
+ columnCosines,
417
+ rowCosines
418
+ } = imagePlaneModule;
419
+
420
+ // we need to get major/minor axis (which are both the same size major = minor)
421
+
422
+ // first axisStart
423
+ const firstAxisStart = esm/* vec3.create */.R3.create();
424
+ esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(firstAxisStart, center, columnCosines, radius);
425
+ const firstAxisEnd = esm/* vec3.create */.R3.create();
426
+ esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(firstAxisEnd, center, columnCosines, -radius);
427
+
428
+ // second axisStart
429
+ const secondAxisStart = esm/* vec3.create */.R3.create();
430
+ esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(secondAxisStart, center, rowCosines, radius);
431
+ const secondAxisEnd = esm/* vec3.create */.R3.create();
432
+ esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(secondAxisEnd, center, rowCosines, -radius);
433
+ renderableData = [firstAxisStart, firstAxisEnd, secondAxisStart, secondAxisEnd];
434
+ break;
435
+ }
436
+ case scoordTypes.ELLIPSE:
437
+ {
438
+ // GraphicData is ordered as [majorAxisStartX, majorAxisStartY, majorAxisEndX, majorAxisEndY, minorAxisStartX, minorAxisStartY, minorAxisEndX, minorAxisEndY]
439
+ // But Cornerstone3D points are ordered as top, bottom, left, right for the
440
+ // ellipse so we need to identify if the majorAxis is horizontal or vertical
441
+ // and then choose the correct points to use for the ellipse.
442
+
443
+ const pointsWorld = [];
444
+ for (let i = 0; i < GraphicData.length; i += 2) {
445
+ const worldPos = core_dist_esm.utilities.imageToWorldCoords(imageId, [GraphicData[i], GraphicData[i + 1]]);
446
+ pointsWorld.push(worldPos);
447
+ }
448
+ const majorAxisStart = esm/* vec3.fromValues */.R3.fromValues(...pointsWorld[0]);
449
+ const majorAxisEnd = esm/* vec3.fromValues */.R3.fromValues(...pointsWorld[1]);
450
+ const minorAxisStart = esm/* vec3.fromValues */.R3.fromValues(...pointsWorld[2]);
451
+ const minorAxisEnd = esm/* vec3.fromValues */.R3.fromValues(...pointsWorld[3]);
452
+ const majorAxisVec = esm/* vec3.create */.R3.create();
453
+ esm/* vec3.sub */.R3.sub(majorAxisVec, majorAxisEnd, majorAxisStart);
454
+
455
+ // normalize majorAxisVec to avoid scaling issues
456
+ esm/* vec3.normalize */.R3.normalize(majorAxisVec, majorAxisVec);
457
+ const minorAxisVec = esm/* vec3.create */.R3.create();
458
+ esm/* vec3.sub */.R3.sub(minorAxisVec, minorAxisEnd, minorAxisStart);
459
+ esm/* vec3.normalize */.R3.normalize(minorAxisVec, minorAxisVec);
460
+ const imagePlaneModule = core_dist_esm.metaData.get('imagePlaneModule', imageId);
461
+ if (!imagePlaneModule) {
462
+ throw new Error('imageId does not have imagePlaneModule metadata');
463
+ }
464
+ const {
465
+ columnCosines
466
+ } = imagePlaneModule;
467
+
468
+ // find which axis is parallel to the columnCosines
469
+ const columnCosinesVec = esm/* vec3.fromValues */.R3.fromValues(...columnCosines);
470
+ const projectedMajorAxisOnColVec = Math.abs(esm/* vec3.dot */.R3.dot(columnCosinesVec, majorAxisVec));
471
+ const projectedMinorAxisOnColVec = Math.abs(esm/* vec3.dot */.R3.dot(columnCosinesVec, minorAxisVec));
472
+ const absoluteOfMajorDotProduct = Math.abs(projectedMajorAxisOnColVec);
473
+ const absoluteOfMinorDotProduct = Math.abs(projectedMinorAxisOnColVec);
474
+ renderableData = [];
475
+ if (Math.abs(absoluteOfMajorDotProduct - 1) < EPSILON) {
476
+ renderableData = [pointsWorld[0], pointsWorld[1], pointsWorld[2], pointsWorld[3]];
477
+ } else if (Math.abs(absoluteOfMinorDotProduct - 1) < EPSILON) {
478
+ renderableData = [pointsWorld[2], pointsWorld[3], pointsWorld[0], pointsWorld[1]];
479
+ } else {
480
+ console.warn('OBLIQUE ELLIPSE NOT YET SUPPORTED');
481
+ }
482
+ break;
483
+ }
484
+ default:
485
+ console.warn('Unsupported GraphicType:', GraphicType);
486
+ }
487
+ return renderableData;
488
+ }
489
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/adapters/dist/@cornerstonejs/adapters.es.js
490
+ var adapters_es = __webpack_require__(4606);
491
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/isRehydratable.js
492
+
493
+ const cornerstoneAdapters = adapters_es.adaptersSR.Cornerstone3D.MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
494
+ const isRehydratable_supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
495
+ const CORNERSTONE_3D_TAG = cornerstoneAdapters.CORNERSTONE_3D_TAG;
496
+
497
+ /**
498
+ * Checks if the given `displaySet`can be rehydrated into the `measurementService`.
499
+ *
500
+ * @param {object} displaySet The SR `displaySet` to check.
501
+ * @param {object[]} mappings The CornerstoneTools 4 mappings to the `measurementService`.
502
+ * @returns {boolean} True if the SR can be rehydrated into the `measurementService`.
503
+ */
504
+ function isRehydratable(displaySet, mappings) {
505
+ if (!mappings || !mappings.length) {
506
+ return false;
507
+ }
508
+ const mappingDefinitions = mappings.map(m => m.annotationType);
509
+ const {
510
+ measurements
511
+ } = displaySet;
512
+ const adapterKeys = Object.keys(cornerstoneAdapters).filter(adapterKey => typeof cornerstoneAdapters[adapterKey].isValidCornerstoneTrackingIdentifier === 'function');
513
+ const adapters = [];
514
+ adapterKeys.forEach(key => {
515
+ if (mappingDefinitions.includes(key)) {
516
+ // Must have both a dcmjs adapter and a measurementService
517
+ // Definition in order to be a candidate for import.
518
+ adapters.push(cornerstoneAdapters[key]);
519
+ }
520
+ });
521
+ for (let i = 0; i < measurements.length; i++) {
522
+ const {
523
+ TrackingIdentifier
524
+ } = measurements[i] || {};
525
+ const hydratable = adapters.some(adapter => {
526
+ let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
527
+ if (isRehydratable_supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
528
+ cornerstoneTag = CORNERSTONE_3D_TAG;
529
+ }
530
+ const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
531
+ return adapter.isValidCornerstoneTrackingIdentifier(mappedTrackingIdentifier);
532
+ });
533
+ if (hydratable) {
534
+ return true;
535
+ }
536
+ console.log('Measurement is not rehydratable', TrackingIdentifier, measurements[i]);
537
+ }
538
+ console.log('No measurements found which were rehydratable');
539
+ return false;
540
+ }
541
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.ts
542
+
543
+
544
+
545
+
546
+
547
+ const {
548
+ CodeScheme: Cornerstone3DCodeScheme
549
+ } = adapters_es.adaptersSR.Cornerstone3D;
550
+ const {
551
+ ImageSet,
552
+ MetadataProvider: metadataProvider
553
+ } = src.classes;
554
+
555
+ // TODO ->
556
+ // Add SR thumbnail
557
+ // Make viewport
558
+ // Get stacks from referenced displayInstanceUID and load into wrapped CornerStone viewport.
559
+
560
+ const sopClassUids = ['1.2.840.10008.5.1.4.1.1.88.11',
561
+ //BASIC_TEXT_SR:
562
+ '1.2.840.10008.5.1.4.1.1.88.22',
563
+ //ENHANCED_SR:
564
+ '1.2.840.10008.5.1.4.1.1.88.33',
565
+ //COMPREHENSIVE_SR:
566
+ '1.2.840.10008.5.1.4.1.1.88.34' //COMPREHENSIVE_3D_SR:
567
+ ];
568
+
569
+ const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
570
+ const CORNERSTONE_3D_TOOLS_SOURCE_VERSION = '0.1';
571
+ const validateSameStudyUID = (uid, instances) => {
572
+ instances.forEach(it => {
573
+ if (it.StudyInstanceUID !== uid) {
574
+ console.warn('Not all instances have the same UID', uid, it);
575
+ throw new Error(`Instances ${it.SOPInstanceUID} does not belong to ${uid}`);
576
+ }
577
+ });
578
+ };
579
+ const CodeNameCodeSequenceValues = {
580
+ ImagingMeasurementReport: '126000',
581
+ ImageLibrary: '111028',
582
+ ImagingMeasurements: '126010',
583
+ MeasurementGroup: '125007',
584
+ ImageLibraryGroup: '126200',
585
+ TrackingUniqueIdentifier: '112040',
586
+ TrackingIdentifier: '112039',
587
+ Finding: '121071',
588
+ FindingSite: 'G-C0E3',
589
+ // SRT
590
+ CornerstoneFreeText: Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT //
591
+ };
592
+
593
+ const CodingSchemeDesignators = {
594
+ SRT: 'SRT',
595
+ CornerstoneCodeSchemes: [Cornerstone3DCodeScheme.CodingSchemeDesignator, 'CST4']
596
+ };
597
+ const RELATIONSHIP_TYPE = {
598
+ INFERRED_FROM: 'INFERRED FROM',
599
+ CONTAINS: 'CONTAINS'
600
+ };
601
+ const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
602
+
603
+ /**
604
+ * Adds instances to the DICOM SR series, rather than creating a new
605
+ * series, so that as SR's are saved, they append to the series, and the
606
+ * key image display set gets updated as well, containing just the new series.
607
+ * @param instances is a list of instances from THIS series that are not
608
+ * in this DICOM SR Display Set already.
609
+ */
610
+ function addInstances(instances, displaySetService) {
611
+ this.instances.push(...instances);
612
+ src.utils.sortStudyInstances(this.instances);
613
+ // The last instance is the newest one, so is the one most interesting.
614
+ // Eventually, the SR viewer should have the ability to choose which SR
615
+ // gets loaded, and to navigate among them.
616
+ this.instance = this.instances[this.instances.length - 1];
617
+ this.isLoaded = false;
618
+ return this;
619
+ }
620
+
621
+ /**
622
+ * DICOM SR SOP Class Handler
623
+ * For all referenced images in the TID 1500/300 sections, add an image to the
624
+ * display.
625
+ * @param instances is a set of instances all from the same series
626
+ * @param servicesManager is the services that can be used for creating
627
+ * @returns The list of display sets created for the given instances object
628
+ */
629
+ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
630
+ // If the series has no instances, stop here
631
+ if (!instances || !instances.length) {
632
+ throw new Error('No instances were provided');
633
+ }
634
+ src.utils.sortStudyInstances(instances);
635
+ // The last instance is the newest one, so is the one most interesting.
636
+ // Eventually, the SR viewer should have the ability to choose which SR
637
+ // gets loaded, and to navigate among them.
638
+ const instance = instances[instances.length - 1];
639
+ const {
640
+ StudyInstanceUID,
641
+ SeriesInstanceUID,
642
+ SOPInstanceUID,
643
+ SeriesDescription,
644
+ SeriesNumber,
645
+ SeriesDate,
646
+ ConceptNameCodeSequence,
647
+ SOPClassUID
648
+ } = instance;
649
+ validateSameStudyUID(instance.StudyInstanceUID, instances);
650
+ if (!ConceptNameCodeSequence || ConceptNameCodeSequence.CodeValue !== CodeNameCodeSequenceValues.ImagingMeasurementReport) {
651
+ servicesManager.services.uiNotificationService.show({
652
+ title: 'DICOM SR',
653
+ message: 'OHIF only supports TID1500 Imaging Measurement Report Structured Reports. The SR you’re trying to view is not supported.',
654
+ type: 'warning',
655
+ duration: 6000
656
+ });
657
+ return [];
658
+ }
659
+ const displaySet = {
660
+ //plugin: id,
661
+ Modality: 'SR',
662
+ displaySetInstanceUID: src.utils.guid(),
663
+ SeriesDescription,
664
+ SeriesNumber,
665
+ SeriesDate,
666
+ SOPInstanceUID,
667
+ SeriesInstanceUID,
668
+ StudyInstanceUID,
669
+ SOPClassHandlerId: SOPClassHandlerId,
670
+ SOPClassUID,
671
+ instances,
672
+ referencedImages: null,
673
+ measurements: null,
674
+ isDerivedDisplaySet: true,
675
+ isLoaded: false,
676
+ sopClassUids,
677
+ instance,
678
+ addInstances
679
+ };
680
+ displaySet.load = () => _load(displaySet, servicesManager, extensionManager);
681
+ return [displaySet];
682
+ }
683
+ function _load(displaySet, servicesManager, extensionManager) {
684
+ const {
685
+ displaySetService,
686
+ measurementService
687
+ } = servicesManager.services;
688
+ const dataSources = extensionManager.getDataSources();
689
+ const dataSource = dataSources[0];
690
+ const {
691
+ ContentSequence
692
+ } = displaySet.instance;
693
+ displaySet.referencedImages = _getReferencedImagesList(ContentSequence);
694
+ displaySet.measurements = _getMeasurements(ContentSequence);
695
+ const mappings = measurementService.getSourceMappings(CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION);
696
+ displaySet.isHydrated = false;
697
+ displaySet.isRehydratable = isRehydratable(displaySet, mappings);
698
+ displaySet.isLoaded = true;
699
+
700
+ // Check currently added displaySets and add measurements if the sources exist.
701
+ displaySetService.activeDisplaySets.forEach(activeDisplaySet => {
702
+ _checkIfCanAddMeasurementsToDisplaySet(displaySet, activeDisplaySet, dataSource);
703
+ });
704
+
705
+ // Subscribe to new displaySets as the source may come in after.
706
+ displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
707
+ const {
708
+ displaySetsAdded
709
+ } = data;
710
+ // If there are still some measurements that have not yet been loaded into cornerstone,
711
+ // See if we can load them onto any of the new displaySets.
712
+ displaySetsAdded.forEach(newDisplaySet => {
713
+ _checkIfCanAddMeasurementsToDisplaySet(displaySet, newDisplaySet, dataSource);
714
+ });
715
+ });
716
+ }
717
+ function _checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, newDisplaySet, dataSource) {
718
+ let unloadedMeasurements = srDisplaySet.measurements.filter(measurement => measurement.loaded === false);
719
+ if (unloadedMeasurements.length === 0) {
720
+ // All already loaded!
721
+ return;
722
+ }
723
+ if (!newDisplaySet instanceof ImageSet) {
724
+ // This also filters out _this_ displaySet, as it is not an ImageSet.
725
+ return;
726
+ }
727
+ const {
728
+ sopClassUids,
729
+ images
730
+ } = newDisplaySet;
731
+
732
+ // Check if any have the newDisplaySet is the correct SOPClass.
733
+ unloadedMeasurements = unloadedMeasurements.filter(measurement => measurement.coords.some(coord => sopClassUids.includes(coord.ReferencedSOPSequence.ReferencedSOPClassUID)));
734
+ if (unloadedMeasurements.length === 0) {
735
+ // New displaySet isn't the correct SOPClass, so can't contain the referenced images.
736
+ return;
737
+ }
738
+ const SOPInstanceUIDs = [];
739
+ unloadedMeasurements.forEach(measurement => {
740
+ const {
741
+ coords
742
+ } = measurement;
743
+ coords.forEach(coord => {
744
+ const SOPInstanceUID = coord.ReferencedSOPSequence.ReferencedSOPInstanceUID;
745
+ if (!SOPInstanceUIDs.includes(SOPInstanceUID)) {
746
+ SOPInstanceUIDs.push(SOPInstanceUID);
747
+ }
748
+ });
749
+ });
750
+ const imageIdsForDisplaySet = dataSource.getImageIdsForDisplaySet(newDisplaySet);
751
+ for (const imageId of imageIdsForDisplaySet) {
752
+ if (!unloadedMeasurements.length) {
753
+ // All measurements loaded.
754
+ return;
755
+ }
756
+ const {
757
+ SOPInstanceUID,
758
+ frameNumber
759
+ } = metadataProvider.getUIDsFromImageID(imageId);
760
+ if (SOPInstanceUIDs.includes(SOPInstanceUID)) {
761
+ for (let j = unloadedMeasurements.length - 1; j >= 0; j--) {
762
+ const measurement = unloadedMeasurements[j];
763
+ if (_measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber)) {
764
+ addMeasurement(measurement, imageId, newDisplaySet.displaySetInstanceUID);
765
+ unloadedMeasurements.splice(j, 1);
766
+ }
767
+ }
768
+ }
769
+ }
770
+ }
771
+ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber) {
772
+ const {
773
+ coords
774
+ } = measurement;
775
+
776
+ // NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM
777
+ // Standard. But for now, we will support only one ReferenceFrameNumber.
778
+ const ReferencedFrameNumber = measurement.coords[0].ReferencedSOPSequence && measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber || 1;
779
+ if (frameNumber && Number(frameNumber) !== Number(ReferencedFrameNumber)) return false;
780
+ for (let j = 0; j < coords.length; j++) {
781
+ const coord = coords[j];
782
+ const {
783
+ ReferencedSOPInstanceUID
784
+ } = coord.ReferencedSOPSequence;
785
+ if (ReferencedSOPInstanceUID === SOPInstanceUID) {
786
+ return true;
787
+ }
788
+ }
789
+ }
790
+ function getSopClassHandlerModule(_ref) {
791
+ let {
792
+ servicesManager,
793
+ extensionManager
794
+ } = _ref;
795
+ const getDisplaySetsFromSeries = instances => {
796
+ return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
797
+ };
798
+ return [{
799
+ name: SOPClassHandlerName,
800
+ sopClassUids,
801
+ getDisplaySetsFromSeries
802
+ }];
803
+ }
804
+ function _getMeasurements(ImagingMeasurementReportContentSequence) {
805
+ const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImagingMeasurements);
806
+ const MeasurementGroups = _getSequenceAsArray(ImagingMeasurements.ContentSequence).filter(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.MeasurementGroup);
807
+ const mergedContentSequencesByTrackingUniqueIdentifiers = _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups);
808
+ const measurements = [];
809
+ Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach(trackingUniqueIdentifier => {
810
+ const mergedContentSequence = mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier];
811
+ const measurement = _processMeasurement(mergedContentSequence);
812
+ if (measurement) {
813
+ measurements.push(measurement);
814
+ }
815
+ });
816
+ return measurements;
817
+ }
818
+ function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups) {
819
+ const mergedContentSequencesByTrackingUniqueIdentifiers = {};
820
+ MeasurementGroups.forEach(MeasurementGroup => {
821
+ const ContentSequence = _getSequenceAsArray(MeasurementGroup.ContentSequence);
822
+ const TrackingUniqueIdentifierItem = ContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.TrackingUniqueIdentifier);
823
+ if (!TrackingUniqueIdentifierItem) {
824
+ console.warn('No Tracking Unique Identifier, skipping ambiguous measurement.');
825
+ }
826
+ const trackingUniqueIdentifier = TrackingUniqueIdentifierItem.UID;
827
+ if (mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier] === undefined) {
828
+ // Add the full ContentSequence
829
+ mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier] = [...ContentSequence];
830
+ } else {
831
+ // Add the ContentSequence minus the tracking identifier, as we have this
832
+ // Information in the merged ContentSequence anyway.
833
+ ContentSequence.forEach(item => {
834
+ if (item.ConceptNameCodeSequence.CodeValue !== CodeNameCodeSequenceValues.TrackingUniqueIdentifier) {
835
+ mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier].push(item);
836
+ }
837
+ });
838
+ }
839
+ });
840
+ return mergedContentSequencesByTrackingUniqueIdentifiers;
841
+ }
842
+ function _processMeasurement(mergedContentSequence) {
843
+ if (mergedContentSequence.some(group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D')) {
844
+ return _processTID1410Measurement(mergedContentSequence);
845
+ }
846
+ return _processNonGeometricallyDefinedMeasurement(mergedContentSequence);
847
+ }
848
+ function _processTID1410Measurement(mergedContentSequence) {
849
+ // Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
850
+ // And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
851
+
852
+ const graphicItem = mergedContentSequence.find(group => group.ValueType === 'SCOORD');
853
+ const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
854
+ const TrackingIdentifierContentItem = mergedContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.TrackingIdentifier);
855
+ if (!graphicItem) {
856
+ console.warn(`graphic ValueType ${graphicItem.ValueType} not currently supported, skipping annotation.`);
857
+ return;
858
+ }
859
+ const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
860
+ const measurement = {
861
+ loaded: false,
862
+ labels: [],
863
+ coords: [_getCoordsFromSCOORDOrSCOORD3D(graphicItem)],
864
+ TrackingUniqueIdentifier: UIDREFContentItem.UID,
865
+ TrackingIdentifier: TrackingIdentifierContentItem.TextValue
866
+ };
867
+ NUMContentItems.forEach(item => {
868
+ const {
869
+ ConceptNameCodeSequence,
870
+ MeasuredValueSequence
871
+ } = item;
872
+ if (MeasuredValueSequence) {
873
+ measurement.labels.push(_getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence));
874
+ }
875
+ });
876
+ return measurement;
877
+ }
878
+ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
879
+ const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
880
+ const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
881
+ const TrackingIdentifierContentItem = mergedContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.TrackingIdentifier);
882
+ const finding = mergedContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.Finding);
883
+ const findingSites = mergedContentSequence.filter(item => item.ConceptNameCodeSequence.CodingSchemeDesignator === CodingSchemeDesignators.SRT && item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.FindingSite);
884
+ const measurement = {
885
+ loaded: false,
886
+ labels: [],
887
+ coords: [],
888
+ TrackingUniqueIdentifier: UIDREFContentItem.UID,
889
+ TrackingIdentifier: TrackingIdentifierContentItem.TextValue
890
+ };
891
+ if (finding && CodingSchemeDesignators.CornerstoneCodeSchemes.includes(finding.ConceptCodeSequence.CodingSchemeDesignator) && finding.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText) {
892
+ measurement.labels.push({
893
+ label: CORNERSTONE_FREETEXT_CODE_VALUE,
894
+ value: finding.ConceptCodeSequence.CodeMeaning
895
+ });
896
+ }
897
+
898
+ // TODO -> Eventually hopefully support SNOMED or some proper code library, just free text for now.
899
+ if (findingSites.length) {
900
+ const cornerstoneFreeTextFindingSite = findingSites.find(FindingSite => CodingSchemeDesignators.CornerstoneCodeSchemes.includes(FindingSite.ConceptCodeSequence.CodingSchemeDesignator) && FindingSite.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText);
901
+ if (cornerstoneFreeTextFindingSite) {
902
+ measurement.labels.push({
903
+ label: CORNERSTONE_FREETEXT_CODE_VALUE,
904
+ value: cornerstoneFreeTextFindingSite.ConceptCodeSequence.CodeMeaning
905
+ });
906
+ }
907
+ }
908
+ NUMContentItems.forEach(item => {
909
+ const {
910
+ ConceptNameCodeSequence,
911
+ ContentSequence,
912
+ MeasuredValueSequence
913
+ } = item;
914
+ const {
915
+ ValueType
916
+ } = ContentSequence;
917
+ if (!ValueType === 'SCOORD') {
918
+ console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
919
+ return;
920
+ }
921
+ const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence);
922
+ if (coords) {
923
+ measurement.coords.push(coords);
924
+ }
925
+ if (MeasuredValueSequence) {
926
+ measurement.labels.push(_getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence));
927
+ }
928
+ });
929
+ return measurement;
930
+ }
931
+ function _getCoordsFromSCOORDOrSCOORD3D(item) {
932
+ const {
933
+ ValueType,
934
+ RelationshipType,
935
+ GraphicType,
936
+ GraphicData
937
+ } = item;
938
+ if (!(RelationshipType == RELATIONSHIP_TYPE.INFERRED_FROM || RelationshipType == RELATIONSHIP_TYPE.CONTAINS)) {
939
+ console.warn(`Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM" or "CONTAINS"`);
940
+ return;
941
+ }
942
+ const coords = {
943
+ ValueType,
944
+ GraphicType,
945
+ GraphicData
946
+ };
947
+
948
+ // ContentSequence has length of 1 as RelationshipType === 'INFERRED FROM'
949
+ if (ValueType === 'SCOORD') {
950
+ const {
951
+ ReferencedSOPSequence
952
+ } = item.ContentSequence;
953
+ coords.ReferencedSOPSequence = ReferencedSOPSequence;
954
+ } else if (ValueType === 'SCOORD3D') {
955
+ const {
956
+ ReferencedFrameOfReferenceSequence
957
+ } = item.ContentSequence;
958
+ coords.ReferencedFrameOfReferenceSequence = ReferencedFrameOfReferenceSequence;
959
+ }
960
+ return coords;
961
+ }
962
+ function _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence) {
963
+ const {
964
+ CodeMeaning
965
+ } = ConceptNameCodeSequence;
966
+ const {
967
+ NumericValue,
968
+ MeasurementUnitsCodeSequence
969
+ } = MeasuredValueSequence;
970
+ const {
971
+ CodeValue
972
+ } = MeasurementUnitsCodeSequence;
973
+ const formatedNumericValue = NumericValue ? Number(NumericValue).toFixed(2) : '';
974
+ return {
975
+ label: CodeMeaning,
976
+ value: `${formatedNumericValue} ${CodeValue}`
977
+ }; // E.g. Long Axis: 31.0 mm
978
+ }
979
+
980
+ function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
981
+ const ImageLibrary = ImagingMeasurementReportContentSequence.find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibrary);
982
+ const ImageLibraryGroup = _getSequenceAsArray(ImageLibrary.ContentSequence).find(item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibraryGroup);
983
+ const referencedImages = [];
984
+ _getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
985
+ const {
986
+ ReferencedSOPSequence
987
+ } = item;
988
+ if (!ReferencedSOPSequence) return;
989
+ for (const ref of _getSequenceAsArray(ReferencedSOPSequence)) {
990
+ if (ref.ReferencedSOPClassUID) {
991
+ const {
992
+ ReferencedSOPClassUID,
993
+ ReferencedSOPInstanceUID
994
+ } = ref;
995
+ referencedImages.push({
996
+ ReferencedSOPClassUID,
997
+ ReferencedSOPInstanceUID
998
+ });
999
+ }
1000
+ }
1001
+ });
1002
+ return referencedImages;
1003
+ }
1004
+ function _getSequenceAsArray(sequence) {
1005
+ if (!sequence) return [];
1006
+ return Array.isArray(sequence) ? sequence : [sequence];
1007
+ }
1008
+ /* harmony default export */ const src_getSopClassHandlerModule = (getSopClassHandlerModule);
1009
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/getHangingProtocolModule.ts
1010
+ const srProtocol = {
1011
+ id: '@ohif/sr',
1012
+ // Don't store this hanging protocol as it applies to the currently active
1013
+ // display set by default
1014
+ // cacheId: null,
1015
+ hasUpdatedPriorsInformation: false,
1016
+ name: 'SR Key Images',
1017
+ // Just apply this one when specifically listed
1018
+ protocolMatchingRules: [],
1019
+ toolGroupIds: ['default'],
1020
+ // -1 would be used to indicate active only, whereas other values are
1021
+ // the number of required priors referenced - so 0 means active with
1022
+ // 0 or more priors.
1023
+ numberOfPriorsReferenced: 0,
1024
+ // Default viewport is used to define the viewport when
1025
+ // additional viewports are added using the layout tool
1026
+ defaultViewport: {
1027
+ viewportOptions: {
1028
+ viewportType: 'stack',
1029
+ toolGroupId: 'default',
1030
+ allowUnmatchedView: true
1031
+ },
1032
+ displaySets: [{
1033
+ id: 'srDisplaySetId',
1034
+ matchedDisplaySetsIndex: -1
1035
+ }]
1036
+ },
1037
+ displaySetSelectors: {
1038
+ srDisplaySetId: {
1039
+ seriesMatchingRules: [{
1040
+ attribute: 'Modality',
1041
+ constraint: {
1042
+ equals: 'SR'
1043
+ }
1044
+ }]
1045
+ }
1046
+ },
1047
+ stages: [{
1048
+ name: 'SR Key Images',
1049
+ viewportStructure: {
1050
+ layoutType: 'grid',
1051
+ properties: {
1052
+ rows: 1,
1053
+ columns: 1
1054
+ }
1055
+ },
1056
+ viewports: [{
1057
+ viewportOptions: {
1058
+ allowUnmatchedView: true
1059
+ },
1060
+ displaySets: [{
1061
+ id: 'srDisplaySetId'
1062
+ }]
1063
+ }]
1064
+ }]
1065
+ };
1066
+ function getHangingProtocolModule() {
1067
+ return [{
1068
+ name: srProtocol.id,
1069
+ protocol: srProtocol
1070
+ }];
1071
+ }
1072
+ /* harmony default export */ const src_getHangingProtocolModule = ((/* unused pure expression or super */ null && (getHangingProtocolModule)));
1073
+
1074
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/onModeEnter.js
1075
+
1076
+ function onModeEnter(_ref) {
1077
+ let {
1078
+ servicesManager
1079
+ } = _ref;
1080
+ const {
1081
+ displaySetService
1082
+ } = servicesManager.services;
1083
+ const displaySetCache = displaySetService.getDisplaySetCache();
1084
+ const srDisplaySets = [...displaySetCache.values()].filter(ds => ds.SOPClassHandlerId === SOPClassHandlerId);
1085
+ srDisplaySets.forEach(ds => {
1086
+ // New mode route, allow SRs to be hydrated again
1087
+ ds.isHydrated = false;
1088
+ });
1089
+ }
1090
+ // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
1091
+ var dcmjs_es = __webpack_require__(22737);
1092
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/getFilteredCornerstoneToolState.ts
1093
+
1094
+
1095
+ const {
1096
+ log
1097
+ } = src["default"];
1098
+ function getFilteredCornerstoneToolState(measurementData, additionalFindingTypes) {
1099
+ const filteredToolState = {};
1100
+ function addToFilteredToolState(annotation, toolType) {
1101
+ if (!annotation.metadata?.referencedImageId) {
1102
+ log.warn(`[DICOMSR] No referencedImageId found for ${toolType} ${annotation.id}`);
1103
+ return;
1104
+ }
1105
+ const imageId = annotation.metadata.referencedImageId;
1106
+ if (!filteredToolState[imageId]) {
1107
+ filteredToolState[imageId] = {};
1108
+ }
1109
+ const imageIdSpecificToolState = filteredToolState[imageId];
1110
+ if (!imageIdSpecificToolState[toolType]) {
1111
+ imageIdSpecificToolState[toolType] = {
1112
+ data: []
1113
+ };
1114
+ }
1115
+ const measurementDataI = measurementData.find(md => md.uid === annotation.annotationUID);
1116
+ const toolData = imageIdSpecificToolState[toolType].data;
1117
+ let {
1118
+ finding
1119
+ } = measurementDataI;
1120
+ const findingSites = [];
1121
+
1122
+ // NOTE -> We use the CORNERSTONEJS coding schemeDesignator which we have
1123
+ // defined in the @cornerstonejs/adapters
1124
+ if (measurementDataI.label) {
1125
+ if (additionalFindingTypes.includes(toolType)) {
1126
+ finding = {
1127
+ CodeValue: 'CORNERSTONEFREETEXT',
1128
+ CodingSchemeDesignator: 'CORNERSTONEJS',
1129
+ CodeMeaning: measurementDataI.label
1130
+ };
1131
+ } else {
1132
+ findingSites.push({
1133
+ CodeValue: 'CORNERSTONEFREETEXT',
1134
+ CodingSchemeDesignator: 'CORNERSTONEJS',
1135
+ CodeMeaning: measurementDataI.label
1136
+ });
1137
+ }
1138
+ }
1139
+ if (measurementDataI.findingSites) {
1140
+ findingSites.push(...measurementDataI.findingSites);
1141
+ }
1142
+ const measurement = Object.assign({}, annotation, {
1143
+ finding,
1144
+ findingSites
1145
+ });
1146
+ toolData.push(measurement);
1147
+ }
1148
+ const uidFilter = measurementData.map(md => md.uid);
1149
+ const uids = uidFilter.slice();
1150
+ const annotationManager = dist_esm.annotation.state.getAnnotationManager();
1151
+ const framesOfReference = annotationManager.getFramesOfReference();
1152
+ for (let i = 0; i < framesOfReference.length; i++) {
1153
+ const frameOfReference = framesOfReference[i];
1154
+ const frameOfReferenceAnnotations = annotationManager.getAnnotations(frameOfReference);
1155
+ const toolTypes = Object.keys(frameOfReferenceAnnotations);
1156
+ for (let j = 0; j < toolTypes.length; j++) {
1157
+ const toolType = toolTypes[j];
1158
+ const annotations = frameOfReferenceAnnotations[toolType];
1159
+ if (annotations) {
1160
+ for (let k = 0; k < annotations.length; k++) {
1161
+ const annotation = annotations[k];
1162
+ const uidIndex = uids.findIndex(uid => uid === annotation.annotationUID);
1163
+ if (uidIndex !== -1) {
1164
+ addToFilteredToolState(annotation, toolType);
1165
+ uids.splice(uidIndex, 1);
1166
+ if (!uids.length) {
1167
+ return filteredToolState;
1168
+ }
1169
+ }
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+ return filteredToolState;
1175
+ }
1176
+ /* harmony default export */ const utils_getFilteredCornerstoneToolState = (getFilteredCornerstoneToolState);
1177
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/commandsModule.js
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+ const {
1184
+ MeasurementReport
1185
+ } = adapters_es.adaptersSR.Cornerstone3D;
1186
+ const {
1187
+ log: commandsModule_log
1188
+ } = src["default"];
1189
+
1190
+ /**
1191
+ *
1192
+ * @param measurementData An array of measurements from the measurements service
1193
+ * that you wish to serialize.
1194
+ * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
1195
+ * @param options Naturalized DICOM JSON headers to merge into the displaySet.
1196
+ *
1197
+ */
1198
+ const _generateReport = function (measurementData, additionalFindingTypes) {
1199
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1200
+ const filteredToolState = utils_getFilteredCornerstoneToolState(measurementData, additionalFindingTypes);
1201
+ const report = MeasurementReport.generateReport(filteredToolState, core_dist_esm.metaData, core_dist_esm.utilities.worldToImageCoords, options);
1202
+ const {
1203
+ dataset
1204
+ } = report;
1205
+
1206
+ // Set the default character set as UTF-8
1207
+ // https://dicom.innolitics.com/ciods/nm-image/sop-common/00080005
1208
+ if (typeof dataset.SpecificCharacterSet === 'undefined') {
1209
+ dataset.SpecificCharacterSet = 'ISO_IR 192';
1210
+ }
1211
+ return dataset;
1212
+ };
1213
+ const commandsModule = _ref => {
1214
+ let {} = _ref;
1215
+ const actions = {
1216
+ /**
1217
+ *
1218
+ * @param measurementData An array of measurements from the measurements service
1219
+ * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
1220
+ * @param options Naturalized DICOM JSON headers to merge into the displaySet.
1221
+ * as opposed to Finding Sites.
1222
+ * that you wish to serialize.
1223
+ */
1224
+ downloadReport: _ref2 => {
1225
+ let {
1226
+ measurementData,
1227
+ additionalFindingTypes,
1228
+ options = {}
1229
+ } = _ref2;
1230
+ const srDataset = actions.generateReport(measurementData, additionalFindingTypes, options);
1231
+ const reportBlob = dcmjs_es["default"].data.datasetToBlob(srDataset);
1232
+
1233
+ //Create a URL for the binary.
1234
+ var objectUrl = URL.createObjectURL(reportBlob);
1235
+ window.location.assign(objectUrl);
1236
+ },
1237
+ /**
1238
+ *
1239
+ * @param measurementData An array of measurements from the measurements service
1240
+ * that you wish to serialize.
1241
+ * @param dataSource The dataSource that you wish to use to persist the data.
1242
+ * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
1243
+ * @param options Naturalized DICOM JSON headers to merge into the displaySet.
1244
+ * @return The naturalized report
1245
+ */
1246
+ storeMeasurements: async _ref3 => {
1247
+ let {
1248
+ measurementData,
1249
+ dataSource,
1250
+ additionalFindingTypes,
1251
+ options = {}
1252
+ } = _ref3;
1253
+ // Use the @cornerstonejs adapter for converting to/from DICOM
1254
+ // But it is good enough for now whilst we only have cornerstone as a datasource.
1255
+ commandsModule_log.info('[DICOMSR] storeMeasurements');
1256
+ if (!dataSource || !dataSource.store || !dataSource.store.dicom) {
1257
+ commandsModule_log.error('[DICOMSR] datasource has no dataSource.store.dicom endpoint!');
1258
+ return Promise.reject({});
1259
+ }
1260
+ try {
1261
+ const naturalizedReport = _generateReport(measurementData, additionalFindingTypes, options);
1262
+ const {
1263
+ StudyInstanceUID,
1264
+ ContentSequence
1265
+ } = naturalizedReport;
1266
+ // The content sequence has 5 or more elements, of which
1267
+ // the `[4]` element contains the annotation data, so this is
1268
+ // checking that there is some annotation data present.
1269
+ if (!ContentSequence?.[4].ContentSequence?.length) {
1270
+ console.log('naturalizedReport missing imaging content', naturalizedReport);
1271
+ throw new Error('Invalid report, no content');
1272
+ }
1273
+ await dataSource.store.dicom(naturalizedReport);
1274
+ if (StudyInstanceUID) {
1275
+ dataSource.deleteStudyMetadataPromise(StudyInstanceUID);
1276
+ }
1277
+
1278
+ // The "Mode" route listens for DicomMetadataStore changes
1279
+ // When a new instance is added, it listens and
1280
+ // automatically calls makeDisplaySets
1281
+ src.DicomMetadataStore.addInstances([naturalizedReport], true);
1282
+ return naturalizedReport;
1283
+ } catch (error) {
1284
+ console.warn(error);
1285
+ commandsModule_log.error(`[DICOMSR] Error while saving the measurements: ${error.message}`);
1286
+ throw new Error(error.message || 'Error while saving the measurements.');
1287
+ }
1288
+ }
1289
+ };
1290
+ const definitions = {
1291
+ downloadReport: {
1292
+ commandFn: actions.downloadReport,
1293
+ storeContexts: [],
1294
+ options: {}
1295
+ },
1296
+ storeMeasurements: {
1297
+ commandFn: actions.storeMeasurements,
1298
+ storeContexts: [],
1299
+ options: {}
1300
+ }
1301
+ };
1302
+ return {
1303
+ actions,
1304
+ definitions,
1305
+ defaultContext: 'CORNERSTONE_STRUCTURED_REPORT'
1306
+ };
1307
+ };
1308
+ /* harmony default export */ const src_commandsModule = (commandsModule);
1309
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/addToolInstance.ts
1310
+
1311
+ function addToolInstance(name, toolClass, configuration) {
1312
+ class InstanceClass extends toolClass {}
1313
+ InstanceClass.toolName = name;
1314
+ (0,dist_esm.addTool)(InstanceClass);
1315
+ }
1316
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/init.ts
1317
+
1318
+
1319
+
1320
+
1321
+
1322
+ /**
1323
+ * @param {object} configuration
1324
+ */
1325
+ function init(_ref) {
1326
+ let {
1327
+ configuration = {}
1328
+ } = _ref;
1329
+ (0,dist_esm.addTool)(DICOMSRDisplayTool);
1330
+ addToolInstance(tools_toolNames.SRLength, dist_esm.LengthTool, {});
1331
+ addToolInstance(tools_toolNames.SRBidirectional, dist_esm.BidirectionalTool);
1332
+ addToolInstance(tools_toolNames.SREllipticalROI, dist_esm.EllipticalROITool);
1333
+ addToolInstance(tools_toolNames.SRCircleROI, dist_esm.CircleROITool);
1334
+ addToolInstance(tools_toolNames.SRArrowAnnotate, dist_esm.ArrowAnnotateTool);
1335
+ addToolInstance(tools_toolNames.SRAngle, dist_esm.AngleTool);
1336
+ // TODO - fix the SR display of Cobb Angle, as it joins the two lines
1337
+ addToolInstance(tools_toolNames.SRCobbAngle, dist_esm.CobbAngleTool);
1338
+ // TODO - fix the rehydration of Freehand, as it throws an exception
1339
+ // on a missing polyline. The fix is probably in CS3D
1340
+ addToolInstance(tools_toolNames.SRPlanarFreehandROI, dist_esm.PlanarFreehandROITool);
1341
+
1342
+ // Modify annotation tools to use dashed lines on SR
1343
+ const dashedLine = {
1344
+ lineDash: '4,4'
1345
+ };
1346
+ dist_esm.annotation.config.style.setToolGroupToolStyles('SRToolGroup', {
1347
+ SRLength: dashedLine,
1348
+ SRBidirectional: dashedLine,
1349
+ SREllipticalROI: dashedLine,
1350
+ SRCircleROI: dashedLine,
1351
+ SRArrowAnnotate: dashedLine,
1352
+ SRCobbAngle: dashedLine,
1353
+ SRAngle: dashedLine,
1354
+ SRPlanarFreehandROI: dashedLine,
1355
+ global: {}
1356
+ });
1357
+ }
1358
+ // EXTERNAL MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js + 1 modules
1359
+ var hydrateStructuredReport = __webpack_require__(68525);
1360
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/createReferencedImageDisplaySet.ts
1361
+
1362
+ const createReferencedImageDisplaySet_ImageSet = src.classes.ImageSet;
1363
+ const findInstance = (measurement, displaySetService) => {
1364
+ const {
1365
+ displaySetInstanceUID,
1366
+ ReferencedSOPInstanceUID: sopUid
1367
+ } = measurement;
1368
+ const referencedDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
1369
+ if (!referencedDisplaySet.images) return;
1370
+ return referencedDisplaySet.images.find(it => it.SOPInstanceUID === sopUid);
1371
+ };
1372
+
1373
+ /** Finds references to display sets inside the measurements
1374
+ * contained within the provided display set.
1375
+ * @return an array of instances referenced.
1376
+ */
1377
+ const findReferencedInstances = (displaySetService, displaySet) => {
1378
+ const instances = [];
1379
+ const instanceById = {};
1380
+ for (const measurement of displaySet.measurements) {
1381
+ const {
1382
+ imageId
1383
+ } = measurement;
1384
+ if (!imageId) continue;
1385
+ if (instanceById[imageId]) continue;
1386
+ const instance = findInstance(measurement, displaySetService);
1387
+ if (!instance) {
1388
+ console.log('Measurement', measurement, 'had no instances found');
1389
+ continue;
1390
+ }
1391
+ instanceById[imageId] = instance;
1392
+ instances.push(instance);
1393
+ }
1394
+ return instances;
1395
+ };
1396
+
1397
+ /**
1398
+ * Creates a new display set containing a single image instance for each
1399
+ * referenced image.
1400
+ *
1401
+ * @param displaySetService
1402
+ * @param displaySet - containing measurements referencing images.
1403
+ * @returns A new (registered/active) display set containing the referenced images
1404
+ */
1405
+ const createReferencedImageDisplaySet = (displaySetService, displaySet) => {
1406
+ const instances = findReferencedInstances(displaySetService, displaySet);
1407
+ // This will be a member function of the created image set
1408
+ const updateInstances = function () {
1409
+ this.images.splice(0, this.images.length, ...findReferencedInstances(displaySetService, displaySet));
1410
+ this.numImageFrames = this.images.length;
1411
+ };
1412
+ const imageSet = new createReferencedImageDisplaySet_ImageSet(instances);
1413
+ const instance = instances[0];
1414
+ imageSet.setAttributes({
1415
+ displaySetInstanceUID: imageSet.uid,
1416
+ // create a local alias for the imageSet UID
1417
+ SeriesDate: instance.SeriesDate,
1418
+ SeriesTime: instance.SeriesTime,
1419
+ SeriesInstanceUID: imageSet.uid,
1420
+ StudyInstanceUID: instance.StudyInstanceUID,
1421
+ SeriesNumber: instance.SeriesNumber || 0,
1422
+ SOPClassUID: instance.SOPClassUID,
1423
+ SeriesDescription: `${displaySet.SeriesDescription} KO ${displaySet.instance.SeriesNumber}`,
1424
+ Modality: 'KO',
1425
+ isMultiFrame: false,
1426
+ numImageFrames: instances.length,
1427
+ SOPClassHandlerId: `@ohif/extension-default.sopClassHandlerModule.stack`,
1428
+ isReconstructable: false,
1429
+ // This object is made of multiple instances from other series
1430
+ isCompositeStack: true,
1431
+ madeInClient: true,
1432
+ excludeFromThumbnailBrowser: true,
1433
+ updateInstances
1434
+ });
1435
+ displaySetService.addDisplaySets(imageSet);
1436
+ return imageSet;
1437
+ };
1438
+ /* harmony default export */ const utils_createReferencedImageDisplaySet = (createReferencedImageDisplaySet);
1439
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/index.tsx
1440
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+
1449
+
1450
+
1451
+ const Component = /*#__PURE__*/react.lazy(() => {
1452
+ return __webpack_require__.e(/* import() */ 953).then(__webpack_require__.bind(__webpack_require__, 27953));
1453
+ });
1454
+ const OHIFCornerstoneSRViewport = props => {
1455
+ return /*#__PURE__*/react.createElement(react.Suspense, {
1456
+ fallback: /*#__PURE__*/react.createElement("div", null, "Loading...")
1457
+ }, /*#__PURE__*/react.createElement(Component, props));
1458
+ };
1459
+
1460
+ /**
1461
+ *
1462
+ */
1463
+ const dicomSRExtension = {
1464
+ /**
1465
+ * Only required property. Should be a unique value across all extensions.
1466
+ */
1467
+ id: id,
1468
+ onModeEnter: onModeEnter,
1469
+ preRegistration: init,
1470
+ /**
1471
+ *
1472
+ *
1473
+ * @param {object} [configuration={}]
1474
+ * @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
1475
+ */
1476
+ getViewportModule(_ref) {
1477
+ let {
1478
+ servicesManager,
1479
+ extensionManager
1480
+ } = _ref;
1481
+ const ExtendedOHIFCornerstoneSRViewport = props => {
1482
+ return /*#__PURE__*/react.createElement(OHIFCornerstoneSRViewport, _extends({
1483
+ servicesManager: servicesManager,
1484
+ extensionManager: extensionManager
1485
+ }, props));
1486
+ };
1487
+ return [{
1488
+ name: 'dicom-sr',
1489
+ component: ExtendedOHIFCornerstoneSRViewport
1490
+ }];
1491
+ },
1492
+ getCommandsModule: src_commandsModule,
1493
+ getSopClassHandlerModule: src_getSopClassHandlerModule,
1494
+ // Include dynmically computed values such as toolNames not known till instantiation
1495
+ getUtilityModule(_ref2) {
1496
+ let {
1497
+ servicesManager
1498
+ } = _ref2;
1499
+ return [{
1500
+ name: 'tools',
1501
+ exports: {
1502
+ toolNames: tools_toolNames
1503
+ }
1504
+ }];
1505
+ }
1506
+ };
1507
+ /* harmony default export */ const cornerstone_dicom_sr_src = (dicomSRExtension);
1508
+
1509
+ // Put static exports here so they can be type checked
1510
+
1511
+
1512
+ /***/ }),
1513
+
1514
+ /***/ 94709:
1515
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1516
+
1517
+ "use strict";
1518
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1519
+ /* harmony export */ "l2": () => (/* binding */ setTrackingUniqueIdentifiersForElement),
1520
+ /* harmony export */ "yR": () => (/* binding */ getTrackingUniqueIdentifiersForElement)
1521
+ /* harmony export */ });
1522
+ /* unused harmony export setActiveTrackingUniqueIdentifierForElement */
1523
+ /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77331);
1524
+
1525
+ const state = {
1526
+ TrackingUniqueIdentifier: null,
1527
+ trackingIdentifiersByViewportId: {}
1528
+ };
1529
+
1530
+ /**
1531
+ * This file is being used to store the per-viewport state of the SR tools,
1532
+ * Since, all the toolStates are added to the cornerstoneTools, when displaying the SRTools,
1533
+ * if there are two viewports rendering the same imageId, we don't want to show
1534
+ * the same SR annotation twice on irrelevant viewport, hence, we are storing the state
1535
+ * of the SR tools in state here, so that we can filter them later.
1536
+ */
1537
+
1538
+ function setTrackingUniqueIdentifiersForElement(element, trackingUniqueIdentifiers) {
1539
+ let activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
1540
+ const enabledElement = (0,_cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__.getEnabledElement)(element);
1541
+ const {
1542
+ viewport
1543
+ } = enabledElement;
1544
+ state.trackingIdentifiersByViewportId[viewport.id] = {
1545
+ trackingUniqueIdentifiers,
1546
+ activeIndex
1547
+ };
1548
+ }
1549
+ function setActiveTrackingUniqueIdentifierForElement(element, TrackingUniqueIdentifier) {
1550
+ const enabledElement = getEnabledElement(element);
1551
+ const {
1552
+ viewport
1553
+ } = enabledElement;
1554
+ const trackingIdentifiersForElement = state.trackingIdentifiersByViewportId[viewport.id];
1555
+ if (trackingIdentifiersForElement) {
1556
+ const activeIndex = trackingIdentifiersForElement.trackingUniqueIdentifiers.findIndex(tuid => tuid === TrackingUniqueIdentifier);
1557
+ trackingIdentifiersForElement.activeIndex = activeIndex;
1558
+ }
1559
+ }
1560
+ function getTrackingUniqueIdentifiersForElement(element) {
1561
+ const enabledElement = (0,_cornerstonejs_core__WEBPACK_IMPORTED_MODULE_0__.getEnabledElement)(element);
1562
+ const {
1563
+ viewport
1564
+ } = enabledElement;
1565
+ if (state.trackingIdentifiersByViewportId[viewport.id]) {
1566
+ return state.trackingIdentifiersByViewportId[viewport.id];
1567
+ }
1568
+ return {
1569
+ trackingUniqueIdentifiers: []
1570
+ };
1571
+ }
1572
+
1573
+
1574
+ /***/ }),
1575
+
1576
+ /***/ 68525:
1577
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1578
+
1579
+ "use strict";
1580
+
1581
+ // EXPORTS
1582
+ __webpack_require__.d(__webpack_exports__, {
1583
+ "Z": () => (/* binding */ hydrateStructuredReport)
1584
+ });
1585
+
1586
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 335 modules
1587
+ var esm = __webpack_require__(77331);
1588
+ // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
1589
+ var src = __webpack_require__(48501);
1590
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/getLabelFromDCMJSImportedToolData.js
1591
+ /**
1592
+ * Extracts the label from the toolData imported from dcmjs. We need to do this
1593
+ * as dcmjs does not depeend on OHIF/the measurementService, it just produces data for cornestoneTools.
1594
+ * This optional data is available for the consumer to process if they wish to.
1595
+ * @param {object} toolData The tooldata relating to the
1596
+ *
1597
+ * @returns {string} The extracted label.
1598
+ */
1599
+ function getLabelFromDCMJSImportedToolData(toolData) {
1600
+ const {
1601
+ findingSites = [],
1602
+ finding
1603
+ } = toolData;
1604
+ let freeTextLabel = findingSites.find(fs => fs.CodeValue === 'CORNERSTONEFREETEXT');
1605
+ if (freeTextLabel) {
1606
+ return freeTextLabel.CodeMeaning;
1607
+ }
1608
+ if (finding && finding.CodeValue === 'CORNERSTONEFREETEXT') {
1609
+ return finding.CodeMeaning;
1610
+ }
1611
+ }
1612
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/adapters/dist/@cornerstonejs/adapters.es.js
1613
+ var adapters_es = __webpack_require__(4606);
1614
+ ;// CONCATENATED MODULE: ../../../extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js
1615
+
1616
+
1617
+
1618
+
1619
+ const {
1620
+ guid
1621
+ } = src["default"].utils;
1622
+ const {
1623
+ MeasurementReport,
1624
+ CORNERSTONE_3D_TAG
1625
+ } = adapters_es.adaptersSR.Cornerstone3D;
1626
+ const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
1627
+ const CORNERSTONE_3D_TOOLS_SOURCE_VERSION = '0.1';
1628
+ const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
1629
+ const convertCode = (codingValues, code) => {
1630
+ if (!code || code.CodingSchemeDesignator === 'CORNERSTONEJS') return;
1631
+ const ref = `${code.CodingSchemeDesignator}:${code.CodeValue}`;
1632
+ const ret = {
1633
+ ...codingValues[ref],
1634
+ ref,
1635
+ ...code,
1636
+ text: code.CodeMeaning
1637
+ };
1638
+ return ret;
1639
+ };
1640
+ const convertSites = (codingValues, sites) => {
1641
+ if (!sites || !sites.length) return;
1642
+ const ret = [];
1643
+ // Do as a loop to convert away from Proxy instances
1644
+ for (let i = 0; i < sites.length; i++) {
1645
+ // Deal with irregular conversion from dcmjs
1646
+ const site = convertCode(codingValues, sites[i][0] || sites[i]);
1647
+ if (site) ret.push(site);
1648
+ }
1649
+ return ret.length && ret || undefined;
1650
+ };
1651
+
1652
+ /**
1653
+ * Hydrates a structured report, for default viewports.
1654
+ *
1655
+ */
1656
+ function hydrateStructuredReport(_ref, displaySetInstanceUID) {
1657
+ let {
1658
+ servicesManager,
1659
+ extensionManager
1660
+ } = _ref;
1661
+ const dataSource = extensionManager.getActiveDataSource()[0];
1662
+ const {
1663
+ measurementService,
1664
+ displaySetService,
1665
+ customizationService
1666
+ } = servicesManager.services;
1667
+ const codingValues = customizationService.getCustomization('codingValues', {});
1668
+ const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
1669
+
1670
+ // TODO -> We should define a strict versioning somewhere.
1671
+ const mappings = measurementService.getSourceMappings(CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION);
1672
+ if (!mappings || !mappings.length) {
1673
+ throw new Error(`Attempting to hydrate measurements service when no mappings present. This shouldn't be reached.`);
1674
+ }
1675
+ const instance = src.DicomMetadataStore.getInstance(displaySet.StudyInstanceUID, displaySet.SeriesInstanceUID, displaySet.SOPInstanceUID);
1676
+ const sopInstanceUIDToImageId = {};
1677
+ const imageIdsForToolState = {};
1678
+ displaySet.measurements.forEach(measurement => {
1679
+ const {
1680
+ ReferencedSOPInstanceUID,
1681
+ imageId,
1682
+ frameNumber
1683
+ } = measurement;
1684
+ if (!sopInstanceUIDToImageId[ReferencedSOPInstanceUID]) {
1685
+ sopInstanceUIDToImageId[ReferencedSOPInstanceUID] = imageId;
1686
+ imageIdsForToolState[ReferencedSOPInstanceUID] = [];
1687
+ }
1688
+ if (!imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber]) {
1689
+ imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber] = imageId;
1690
+ }
1691
+ });
1692
+ const datasetToUse = _mapLegacyDataSet(instance);
1693
+
1694
+ // Use dcmjs to generate toolState.
1695
+ const storedMeasurementByAnnotationType = MeasurementReport.generateToolState(datasetToUse,
1696
+ // NOTE: we need to pass in the imageIds to dcmjs since the we use them
1697
+ // for the imageToWorld transformation. The following assumes that the order
1698
+ // that measurements were added to the display set are the same order as
1699
+ // the measurementGroups in the instance.
1700
+ sopInstanceUIDToImageId, esm.utilities.imageToWorldCoords, esm.metaData);
1701
+
1702
+ // Filter what is found by DICOM SR to measurements we support.
1703
+ const mappingDefinitions = mappings.map(m => m.annotationType);
1704
+ const hydratableMeasurementsInSR = {};
1705
+ Object.keys(storedMeasurementByAnnotationType).forEach(key => {
1706
+ if (mappingDefinitions.includes(key)) {
1707
+ hydratableMeasurementsInSR[key] = storedMeasurementByAnnotationType[key];
1708
+ }
1709
+ });
1710
+
1711
+ // Set the series touched as tracked.
1712
+ const imageIds = [];
1713
+
1714
+ // TODO: notification if no hydratable?
1715
+ Object.keys(hydratableMeasurementsInSR).forEach(annotationType => {
1716
+ const toolDataForAnnotationType = hydratableMeasurementsInSR[annotationType];
1717
+ toolDataForAnnotationType.forEach(toolData => {
1718
+ // Add the measurement to toolState
1719
+ // dcmjs and Cornerstone3D has structural defect in supporting multi-frame
1720
+ // files, and looking up the imageId from sopInstanceUIDToImageId results
1721
+ // in the wrong value.
1722
+ const frameNumber = toolData.annotation.data && toolData.annotation.data.frameNumber || 1;
1723
+ const imageId = imageIdsForToolState[toolData.sopInstanceUid][frameNumber] || sopInstanceUIDToImageId[toolData.sopInstanceUid];
1724
+ if (!imageIds.includes(imageId)) {
1725
+ imageIds.push(imageId);
1726
+ }
1727
+ });
1728
+ });
1729
+ let targetStudyInstanceUID;
1730
+ const SeriesInstanceUIDs = [];
1731
+ for (let i = 0; i < imageIds.length; i++) {
1732
+ const imageId = imageIds[i];
1733
+ const {
1734
+ SeriesInstanceUID,
1735
+ StudyInstanceUID
1736
+ } = esm.metaData.get('instance', imageId);
1737
+ if (!SeriesInstanceUIDs.includes(SeriesInstanceUID)) {
1738
+ SeriesInstanceUIDs.push(SeriesInstanceUID);
1739
+ }
1740
+ if (!targetStudyInstanceUID) {
1741
+ targetStudyInstanceUID = StudyInstanceUID;
1742
+ } else if (targetStudyInstanceUID !== StudyInstanceUID) {
1743
+ console.warn('NO SUPPORT FOR SRs THAT HAVE MEASUREMENTS FROM MULTIPLE STUDIES.');
1744
+ }
1745
+ }
1746
+ Object.keys(hydratableMeasurementsInSR).forEach(annotationType => {
1747
+ const toolDataForAnnotationType = hydratableMeasurementsInSR[annotationType];
1748
+ toolDataForAnnotationType.forEach(toolData => {
1749
+ // Add the measurement to toolState
1750
+ // dcmjs and Cornerstone3D has structural defect in supporting multi-frame
1751
+ // files, and looking up the imageId from sopInstanceUIDToImageId results
1752
+ // in the wrong value.
1753
+ const frameNumber = toolData.annotation.data && toolData.annotation.data.frameNumber || 1;
1754
+ const imageId = imageIdsForToolState[toolData.sopInstanceUid][frameNumber] || sopInstanceUIDToImageId[toolData.sopInstanceUid];
1755
+ toolData.uid = guid();
1756
+ const instance = esm.metaData.get('instance', imageId);
1757
+ const {
1758
+ FrameOfReferenceUID
1759
+ // SOPInstanceUID,
1760
+ // SeriesInstanceUID,
1761
+ // StudyInstanceUID,
1762
+ } = instance;
1763
+ const annotation = {
1764
+ annotationUID: toolData.annotation.annotationUID,
1765
+ data: toolData.annotation.data,
1766
+ metadata: {
1767
+ toolName: annotationType,
1768
+ referencedImageId: imageId,
1769
+ FrameOfReferenceUID
1770
+ }
1771
+ };
1772
+ const source = measurementService.getSource(CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION);
1773
+ annotation.data.label = getLabelFromDCMJSImportedToolData(toolData);
1774
+ annotation.data.finding = convertCode(codingValues, toolData.finding?.[0]);
1775
+ annotation.data.findingSites = convertSites(codingValues, toolData.findingSites);
1776
+ annotation.data.site = annotation.data.findingSites?.[0];
1777
+ const matchingMapping = mappings.find(m => m.annotationType === annotationType);
1778
+ measurementService.addRawMeasurement(source, annotationType, {
1779
+ annotation
1780
+ }, matchingMapping.toMeasurementSchema, dataSource);
1781
+ if (!imageIds.includes(imageId)) {
1782
+ imageIds.push(imageId);
1783
+ }
1784
+ });
1785
+ });
1786
+ displaySet.isHydrated = true;
1787
+ return {
1788
+ StudyInstanceUID: targetStudyInstanceUID,
1789
+ SeriesInstanceUIDs
1790
+ };
1791
+ }
1792
+ function _mapLegacyDataSet(dataset) {
1793
+ const REPORT = 'Imaging Measurements';
1794
+ const GROUP = 'Measurement Group';
1795
+ const TRACKING_IDENTIFIER = 'Tracking Identifier';
1796
+
1797
+ // Identify the Imaging Measurements
1798
+ const imagingMeasurementContent = toArray(dataset.ContentSequence).find(codeMeaningEquals(REPORT));
1799
+
1800
+ // Retrieve the Measurements themselves
1801
+ const measurementGroups = toArray(imagingMeasurementContent.ContentSequence).filter(codeMeaningEquals(GROUP));
1802
+
1803
+ // For each of the supported measurement types, compute the measurement data
1804
+ const measurementData = {};
1805
+ const cornerstoneToolClasses = MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
1806
+ const registeredToolClasses = [];
1807
+ Object.keys(cornerstoneToolClasses).forEach(key => {
1808
+ registeredToolClasses.push(cornerstoneToolClasses[key]);
1809
+ measurementData[key] = [];
1810
+ });
1811
+ measurementGroups.forEach((measurementGroup, index) => {
1812
+ const measurementGroupContentSequence = toArray(measurementGroup.ContentSequence);
1813
+ const TrackingIdentifierGroup = measurementGroupContentSequence.find(contentItem => contentItem.ConceptNameCodeSequence.CodeMeaning === TRACKING_IDENTIFIER);
1814
+ const TrackingIdentifier = TrackingIdentifierGroup.TextValue;
1815
+ let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
1816
+ if (supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
1817
+ cornerstoneTag = CORNERSTONE_3D_TAG;
1818
+ }
1819
+ const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
1820
+ TrackingIdentifierGroup.TextValue = mappedTrackingIdentifier;
1821
+ });
1822
+ return dataset;
1823
+ }
1824
+ const toArray = function (x) {
1825
+ return Array.isArray(x) ? x : [x];
1826
+ };
1827
+ const codeMeaningEquals = codeMeaningName => {
1828
+ return contentItem => {
1829
+ return contentItem.ConceptNameCodeSequence.CodeMeaning === codeMeaningName;
1830
+ };
1831
+ };
1832
+
1833
+ /***/ }),
1834
+
1835
+ /***/ 78753:
1836
+ /***/ (() => {
1837
+
1838
+ /* (ignored) */
1839
+
1840
+ /***/ })
1841
+
1842
+ }]);