@ohif/app 3.8.0-beta.8 → 3.8.0-beta.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{220.bundle.f7e1c96c94245e70f2be.js → 109.bundle.b4fee2a22b622839baf5.js} +4466 -3715
- package/dist/{471.bundle.49c8d281adbae4a2c4df.js → 121.bundle.47f05840a5b3cdf75543.js} +94 -113
- package/dist/141.bundle.556b4c1e4cab770417ac.js +8620 -0
- package/dist/{687.bundle.9065db35c01823286f08.js → 164.bundle.fadc7c5d634402c73b5f.js} +22 -38
- package/dist/17dd54813d5acc10bf8f.wasm +0 -0
- package/dist/183.bundle.a3e238998be71c4b2af8.js +30410 -0
- package/dist/{506.bundle.5731bb4349e266491225.js → 188.bundle.51dc4b37920f45594393.js} +23 -28
- package/dist/{342.bundle.e7c3d500f86fdfcc62b5.js → 206.bundle.fcaa081a0d1f68095c31.js} +1991 -1145
- package/dist/20fc4c659b85ccd2a9c0.wasm +0 -0
- package/dist/217.bundle.d44bbaa50b6fa563fe15.js +115126 -0
- package/dist/{451.bundle.57c21db5d003c75e9d61.js → 295.bundle.5ace95771ced62bdcab8.js} +111 -128
- package/dist/{125.bundle.253395f320b72180da63.js → 297.bundle.194d8985ab974839b5b6.js} +7 -8
- package/dist/{19.bundle.f77c5787b6d8ac0b638b.js → 325.bundle.fd8e0c18db4708d03a91.js} +477 -373
- package/dist/335.bundle.8400aa5a88697a6b9d53.js +2590 -0
- package/dist/{202.bundle.d3490836f71e001dd30f.js → 342.bundle.e6d0bba29351b5650a8c.js} +566 -868
- package/dist/{776.bundle.a2dedb405a12ffd7699b.js → 41.bundle.0905b258a90a7c6437bb.js} +7453 -3624
- package/dist/422.bundle.c6fd037b075dd54f1ba7.js +865 -0
- package/dist/{957.bundle.9ea4506963ef8b2d84ba.js → 433.bundle.e0018820758f5a86fa7f.js} +14797 -27561
- package/dist/445.bundle.38c6d2af64e41cd7c614.js +7835 -0
- package/dist/{126.bundle.6e7111d58bcc937ffd80.js → 448.bundle.5e6da31477887bf53016.js} +356 -430
- package/dist/487.bundle.89d973049defb3ba6cb7.js +1876 -0
- package/dist/{886.bundle.c8dd3ecc42a4253de278.js → 530.bundle.207b38c15c4c01e4db0e.js} +104 -121
- package/dist/{250.bundle.aea3335667054bdefe36.js → 544.bundle.1c1f57118560046649c1.js} +37 -62
- package/dist/574.bundle.d648fea691d6709bf2b4.js +2652 -0
- package/dist/{181.css → 574.css} +1 -1
- package/dist/{410.bundle.15c855b0ff4a1a674fb8.js → 594.bundle.84076375b127b9c7f673.js} +183 -221
- package/dist/{221.bundle.aef554202c58483cb34e.js → 633.bundle.acab89baaa06a299d679.js} +365 -553
- package/dist/{774.bundle.4b2dc46a35012b898e1a.js → 644.bundle.1e77691d2eeb96a423b0.js} +1852 -8945
- package/dist/{663.bundle.d7be28450db14266cdd0.js → 669.bundle.b17e8a621e38d92c653f.js} +310 -265
- package/dist/699.bundle.9367d7ef9f7615b2e733.js +772 -0
- package/dist/702.bundle.963481fbf871984b646f.js +8426 -0
- package/dist/722.bundle.afab1fe6bfcd569130ac.js +1083 -0
- package/dist/{359.bundle.45ecb3d28e8c22142606.js → 724.bundle.55f9f49816de931af91a.js} +165 -260
- package/dist/{757.bundle.ec8301d8e70d2b990f65.js → 726.bundle.0b3d9277d22fe7e15b89.js} +512 -879
- package/dist/{530.bundle.a03b6f942ace3e1baa1e.js → 835.bundle.15aff0b7433bb0dd6d6d.js} +37 -30
- package/dist/{822.bundle.82cdc418f8f56da6060b.js → 862.bundle.d32ab08e64806b2e964d.js} +81 -97
- package/dist/{236.bundle.4e9924934a747afac132.js → 889.bundle.8ef8b723d0163d5d135c.js} +207 -199
- package/dist/{281.bundle.deb7492d143e7768d8bf.js → 905.bundle.8a96e1a75b7cfe5ec093.js} +157 -124
- package/dist/{814.bundle.c8c951d20039b63b865a.js → 907.bundle.5c88ed911bed18582da4.js} +16 -30
- package/dist/{417.bundle.af0a207c29b109f84159.js → 931.bundle.d270a1fda9a2836c3cc5.js} +26 -26
- package/dist/{686.bundle.dccef1f36e4bc79bcc48.js → 939.bundle.9d93b2e47c52338747a2.js} +7 -8
- package/dist/94.bundle.f5f2479c214180d05d42.js +778 -0
- package/dist/{12.bundle.b5ca13e5363f170ecb3b.js → 961.bundle.f4e52bc76d3044d05372.js} +20 -33
- package/dist/app-config.js +1 -0
- package/dist/app.bundle.css +16 -13
- package/dist/{app.bundle.a978edc59b9d82f2eb22.js → app.bundle.ed937512f7d19d61c411.js} +183396 -87682
- package/dist/assets/images/CT-AAA.png +0 -0
- package/dist/assets/images/CT-AAA2.png +0 -0
- package/dist/assets/images/CT-Air.png +0 -0
- package/dist/assets/images/CT-Bone.png +0 -0
- package/dist/assets/images/CT-Bones.png +0 -0
- package/dist/assets/images/CT-Cardiac.png +0 -0
- package/dist/assets/images/CT-Cardiac2.png +0 -0
- package/dist/assets/images/CT-Cardiac3.png +0 -0
- package/dist/assets/images/CT-Chest-Contrast-Enhanced.png +0 -0
- package/dist/assets/images/CT-Chest-Vessels.png +0 -0
- package/dist/assets/images/CT-Coronary-Arteries-2.png +0 -0
- package/dist/assets/images/CT-Coronary-Arteries-3.png +0 -0
- package/dist/assets/images/CT-Coronary-Arteries.png +0 -0
- package/dist/assets/images/CT-Cropped-Volume-Bone.png +0 -0
- package/dist/assets/images/CT-Fat.png +0 -0
- package/dist/assets/images/CT-Liver-Vasculature.png +0 -0
- package/dist/assets/images/CT-Lung.png +0 -0
- package/dist/assets/images/CT-MIP.png +0 -0
- package/dist/assets/images/CT-Muscle.png +0 -0
- package/dist/assets/images/CT-Pulmonary-Arteries.png +0 -0
- package/dist/assets/images/CT-Soft-Tissue.png +0 -0
- package/dist/assets/images/DTI-FA-Brain.png +0 -0
- package/dist/assets/images/MR-Angio.png +0 -0
- package/dist/assets/images/MR-Default.png +0 -0
- package/dist/assets/images/MR-MIP.png +0 -0
- package/dist/assets/images/MR-T2-Brain.png +0 -0
- package/dist/assets/images/VolumeRendering.png +0 -0
- package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
- package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
- package/dist/{dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js → dicom-microscopy-viewer.bundle.d3a56dc9f62df5e11019.js} +3 -3
- package/dist/histogram-worker.bundle.829e14ec12c2b41a4323.js +359 -0
- package/dist/index.html +1 -1
- package/dist/{index.worker.e62ecca63f1a2e124230.worker.js → index.worker.64c896c4316fcd506666.worker.js} +2 -2
- package/dist/index.worker.64c896c4316fcd506666.worker.js.map +1 -0
- package/dist/polySeg.bundle.f1a6ece1396dc1385155.js +249 -0
- package/dist/serve.json +12 -0
- package/dist/sw.js +1 -1
- package/package.json +26 -22
- package/dist/181.bundle.a62b9f0ec692299acb35.js +0 -1527
- package/dist/23.bundle.e008ad788170f2ed5569.js +0 -900
- package/dist/604.bundle.a51f83e64004bca5f497.js +0 -1848
- package/dist/613.bundle.9e7072e5b575354fe51e.js +0 -532
- package/dist/743.bundle.489f7df3a089d4d374e1.js +0 -78007
- package/dist/75788f12450d4c5ed494.wasm +0 -0
- package/dist/775.bundle.2285e7e0e67878948c0d.js +0 -1009
- package/dist/788.bundle.207ac23c0dfa70cbe3fb.js +0 -2682
- package/dist/82.bundle.d6fdcca0f67540bb226a.js +0 -1049
- package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +0 -1
- /package/dist/{19.css → 325.css} +0 -0
- /package/dist/{776.css → 41.css} +0 -0
- /package/dist/{579.css → 481.css} +0 -0
- /package/dist/{250.css → 544.css} +0 -0
- /package/dist/{221.css → 633.css} +0 -0
|
@@ -0,0 +1,1876 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[487],{
|
|
3
|
+
|
|
4
|
+
/***/ 26487:
|
|
5
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
6
|
+
|
|
7
|
+
// ESM COMPAT FLAG
|
|
8
|
+
__webpack_require__.r(__webpack_exports__);
|
|
9
|
+
|
|
10
|
+
// EXPORTS
|
|
11
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
12
|
+
"default": () => (/* binding */ dynamicVolumeExtension)
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// NAMESPACE OBJECT: ../../../extensions/cornerstone-dynamic-volume/src/actions/index.ts
|
|
16
|
+
var actions_namespaceObject = {};
|
|
17
|
+
__webpack_require__.r(actions_namespaceObject);
|
|
18
|
+
__webpack_require__.d(actions_namespaceObject, {
|
|
19
|
+
updateSegmentationsChartDisplaySet: () => (updateSegmentationsChartDisplaySet)
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/package.json
|
|
23
|
+
const package_namespaceObject = /*#__PURE__*/JSON.parse('{"UU":"@ohif/extension-cornerstone-dynamic-volume"}');
|
|
24
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/id.js
|
|
25
|
+
|
|
26
|
+
const id = package_namespaceObject.UU;
|
|
27
|
+
const SOPClassHandlerName = 'dynamic-volume';
|
|
28
|
+
|
|
29
|
+
// EXTERNAL MODULE: ../../core/src/index.ts + 70 modules
|
|
30
|
+
var src = __webpack_require__(55411);
|
|
31
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 327 modules
|
|
32
|
+
var esm = __webpack_require__(44656);
|
|
33
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/tools/dist/esm/index.js + 18 modules
|
|
34
|
+
var dist_esm = __webpack_require__(24542);
|
|
35
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/actions/updateSegmentationsChartDisplaySet.ts
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
const CHART_MODALITY = 'CHT';
|
|
40
|
+
const SEG_CHART_INSTANCE_UID = src.utils.guid();
|
|
41
|
+
|
|
42
|
+
// Private SOPClassUid for chart data
|
|
43
|
+
const ChartDataSOPClassUid = '1.9.451.13215.7.3.2.7.6.1';
|
|
44
|
+
const {
|
|
45
|
+
utilities: csToolsUtils
|
|
46
|
+
} = dist_esm;
|
|
47
|
+
function _getDateTimeStr() {
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const date = now.getFullYear() + ('0' + now.getUTCMonth()).slice(-2) + ('0' + now.getUTCDate()).slice(-2);
|
|
50
|
+
const time = ('0' + now.getUTCHours()).slice(-2) + ('0' + now.getUTCMinutes()).slice(-2) + ('0' + now.getUTCSeconds()).slice(-2);
|
|
51
|
+
return {
|
|
52
|
+
date,
|
|
53
|
+
time
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function _getTimePointsDataByTagName(volume, timePointsTag) {
|
|
57
|
+
const uniqueTimePoints = volume.imageIds.reduce((timePoints, imageId) => {
|
|
58
|
+
const instance = src.DicomMetadataStore.getInstanceByImageId(imageId);
|
|
59
|
+
const timePointValue = instance[timePointsTag];
|
|
60
|
+
if (timePointValue !== undefined) {
|
|
61
|
+
timePoints.add(timePointValue);
|
|
62
|
+
}
|
|
63
|
+
return timePoints;
|
|
64
|
+
}, new Set());
|
|
65
|
+
return Array.from(uniqueTimePoints).sort((a, b) => a - b);
|
|
66
|
+
}
|
|
67
|
+
function _convertTimePointsUnit(timePoints, timePointsUnit) {
|
|
68
|
+
const validUnits = ['ms', 's', 'm', 'h'];
|
|
69
|
+
const divisors = [1000, 60, 60];
|
|
70
|
+
const currentUnitIndex = validUnits.indexOf(timePointsUnit);
|
|
71
|
+
let divisor = 1;
|
|
72
|
+
if (currentUnitIndex !== -1) {
|
|
73
|
+
for (let i = currentUnitIndex; i < validUnits.length - 1; i++) {
|
|
74
|
+
const newDivisor = divisor * divisors[i];
|
|
75
|
+
const greaterThanDivisorCount = timePoints.filter(timePoint => timePoint > newDivisor).length;
|
|
76
|
+
|
|
77
|
+
// Change the scale only if more than 50% of the time points are
|
|
78
|
+
// greater than the new divisor.
|
|
79
|
+
if (greaterThanDivisorCount <= timePoints.length / 2) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
divisor = newDivisor;
|
|
83
|
+
timePointsUnit = validUnits[i + 1];
|
|
84
|
+
}
|
|
85
|
+
if (divisor > 1) {
|
|
86
|
+
timePoints = timePoints.map(timePoint => timePoint / divisor);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
timePoints,
|
|
91
|
+
timePointsUnit
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// It currently supports only one tag but a few other will be added soon
|
|
96
|
+
// Supported 4D Tags
|
|
97
|
+
// (0018,1060) Trigger Time [NOK]
|
|
98
|
+
// (0018,0081) Echo Time [NOK]
|
|
99
|
+
// (0018,0086) Echo Number [NOK]
|
|
100
|
+
// (0020,0100) Temporal Position Identifier [NOK]
|
|
101
|
+
// (0054,1300) FrameReferenceTime [OK]
|
|
102
|
+
function _getTimePointsData(volume) {
|
|
103
|
+
const timePointsTags = {
|
|
104
|
+
FrameReferenceTime: {
|
|
105
|
+
unit: 'ms'
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const timePointsTagNames = Object.keys(timePointsTags);
|
|
109
|
+
let timePoints;
|
|
110
|
+
let timePointsUnit;
|
|
111
|
+
for (let i = 0; i < timePointsTagNames.length; i++) {
|
|
112
|
+
const tagName = timePointsTagNames[i];
|
|
113
|
+
const curTimePoints = _getTimePointsDataByTagName(volume, tagName);
|
|
114
|
+
if (curTimePoints.length) {
|
|
115
|
+
timePoints = curTimePoints;
|
|
116
|
+
timePointsUnit = timePointsTags[tagName].unit;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!timePoints.length) {
|
|
121
|
+
const concatTagNames = timePointsTagNames.join(', ');
|
|
122
|
+
throw new Error(`Could not extract time points data for the following tags: ${concatTagNames}`);
|
|
123
|
+
}
|
|
124
|
+
const convertedTimePoints = _convertTimePointsUnit(timePoints, timePointsUnit);
|
|
125
|
+
timePoints = convertedTimePoints.timePoints;
|
|
126
|
+
timePointsUnit = convertedTimePoints.timePointsUnit;
|
|
127
|
+
return {
|
|
128
|
+
timePoints,
|
|
129
|
+
timePointsUnit
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function _getSegmentationData(segmentation, volumesTimePointsCache, displaySetService) {
|
|
133
|
+
const displaySets = displaySetService.getActiveDisplaySets();
|
|
134
|
+
const dynamic4DDisplaySet = displaySets.find(displaySet => {
|
|
135
|
+
const anInstance = displaySet.instances?.[0];
|
|
136
|
+
if (anInstance) {
|
|
137
|
+
return anInstance.FrameReferenceTime !== undefined || anInstance.NumberOfTimeSlices !== undefined;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// const referencedDynamicVolume = cs.cache.getVolume(dynamic4DDisplaySet.displaySetInstanceUID);
|
|
143
|
+
let volumeCacheKey;
|
|
144
|
+
const volumeId = dynamic4DDisplaySet.displaySetInstanceUID;
|
|
145
|
+
for (const [key] of esm.cache._volumeCache) {
|
|
146
|
+
if (key.includes(volumeId)) {
|
|
147
|
+
volumeCacheKey = key;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
let referencedDynamicVolume;
|
|
152
|
+
if (volumeCacheKey) {
|
|
153
|
+
referencedDynamicVolume = esm.cache.getVolume(volumeCacheKey);
|
|
154
|
+
}
|
|
155
|
+
const {
|
|
156
|
+
StudyInstanceUID,
|
|
157
|
+
StudyDescription
|
|
158
|
+
} = src.DicomMetadataStore.getInstanceByImageId(referencedDynamicVolume.imageIds[0]);
|
|
159
|
+
const [timeData, _] = csToolsUtils.dynamicVolume.getDataInTime(referencedDynamicVolume, {
|
|
160
|
+
maskVolumeId: segmentation.id
|
|
161
|
+
});
|
|
162
|
+
const pixelCount = timeData.length;
|
|
163
|
+
if (pixelCount === 0) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// since we only use one segmentation representation per segmentationId
|
|
168
|
+
// it is fine to pick the first one
|
|
169
|
+
const segmentationRepresentations = dist_esm.segmentation.state.getSegmentationIdRepresentations(segmentation.id);
|
|
170
|
+
const segmentationRepresentationUID = segmentationRepresentations[0].segmentationRepresentationUID;
|
|
171
|
+
const toolGroupId = dist_esm.segmentation.state.getToolGroupIdFromSegmentationRepresentationUID(segmentationRepresentationUID);
|
|
172
|
+
|
|
173
|
+
// Todo: this is useless we should be able to grab color with just segRepUID and segmentIndex
|
|
174
|
+
const color = dist_esm.segmentation.config.color.getColorForSegmentIndex(toolGroupId, segmentationRepresentationUID, 1 // segmentIndex
|
|
175
|
+
);
|
|
176
|
+
const hexColor = esm.utilities.color.rgbToHex(...color);
|
|
177
|
+
let timePointsData = volumesTimePointsCache.get(referencedDynamicVolume);
|
|
178
|
+
if (!timePointsData) {
|
|
179
|
+
timePointsData = _getTimePointsData(referencedDynamicVolume);
|
|
180
|
+
volumesTimePointsCache.set(referencedDynamicVolume, timePointsData);
|
|
181
|
+
}
|
|
182
|
+
const {
|
|
183
|
+
timePoints,
|
|
184
|
+
timePointsUnit
|
|
185
|
+
} = timePointsData;
|
|
186
|
+
if (timePoints.length !== timeData[0].length) {
|
|
187
|
+
throw new Error('Invalid number of time points returned');
|
|
188
|
+
}
|
|
189
|
+
const timepointsCount = timePoints.length;
|
|
190
|
+
const chartSeriesData = new Array(timepointsCount);
|
|
191
|
+
for (let i = 0; i < timepointsCount; i++) {
|
|
192
|
+
const average = timeData.reduce((acc, cur) => acc + cur[i] / pixelCount, 0);
|
|
193
|
+
chartSeriesData[i] = [timePoints[i], average];
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
StudyInstanceUID,
|
|
197
|
+
StudyDescription,
|
|
198
|
+
chartData: {
|
|
199
|
+
series: {
|
|
200
|
+
label: segmentation.label,
|
|
201
|
+
points: chartSeriesData,
|
|
202
|
+
color: hexColor
|
|
203
|
+
},
|
|
204
|
+
axis: {
|
|
205
|
+
x: {
|
|
206
|
+
label: `Time (${timePointsUnit})`
|
|
207
|
+
},
|
|
208
|
+
y: {
|
|
209
|
+
label: `Vl (Bq/ml)`
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function _getInstanceFromSegmentations(segmentations, displaySetService) {
|
|
216
|
+
if (!segmentations.length) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const volumesTimePointsCache = new WeakMap();
|
|
220
|
+
const segmentationsData = segmentations.map(segmentation => _getSegmentationData(segmentation, volumesTimePointsCache, displaySetService));
|
|
221
|
+
const {
|
|
222
|
+
date: seriesDate,
|
|
223
|
+
time: seriesTime
|
|
224
|
+
} = _getDateTimeStr();
|
|
225
|
+
const series = segmentationsData.reduce((allSeries, curSegData) => {
|
|
226
|
+
return [...allSeries, curSegData.chartData.series];
|
|
227
|
+
}, []);
|
|
228
|
+
const instance = {
|
|
229
|
+
SOPClassUID: ChartDataSOPClassUid,
|
|
230
|
+
Modality: CHART_MODALITY,
|
|
231
|
+
SOPInstanceUID: src.utils.guid(),
|
|
232
|
+
SeriesDate: seriesDate,
|
|
233
|
+
SeriesTime: seriesTime,
|
|
234
|
+
SeriesInstanceUID: SEG_CHART_INSTANCE_UID,
|
|
235
|
+
StudyInstanceUID: segmentationsData[0].StudyInstanceUID,
|
|
236
|
+
StudyDescription: segmentationsData[0].StudyDescription,
|
|
237
|
+
SeriesNumber: 100,
|
|
238
|
+
SeriesDescription: 'Segmentation chart series data',
|
|
239
|
+
chartData: {
|
|
240
|
+
series,
|
|
241
|
+
axis: {
|
|
242
|
+
...segmentationsData[0].chartData.axis
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const seriesMetadata = {
|
|
247
|
+
StudyInstanceUID: instance.StudyInstanceUID,
|
|
248
|
+
StudyDescription: instance.StudyDescription,
|
|
249
|
+
SeriesInstanceUID: instance.SeriesInstanceUID,
|
|
250
|
+
SeriesDescription: instance.SeriesDescription,
|
|
251
|
+
SeriesNumber: instance.SeriesNumber,
|
|
252
|
+
SeriesTime: instance.SeriesTime,
|
|
253
|
+
SOPClassUID: instance.SOPClassUID,
|
|
254
|
+
Modality: instance.Modality
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
seriesMetadata,
|
|
258
|
+
instance
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function updateSegmentationsChartDisplaySet({
|
|
262
|
+
servicesManager
|
|
263
|
+
}) {
|
|
264
|
+
const {
|
|
265
|
+
segmentationService,
|
|
266
|
+
displaySetService
|
|
267
|
+
} = servicesManager.services;
|
|
268
|
+
const segmentations = segmentationService.getSegmentations();
|
|
269
|
+
const {
|
|
270
|
+
seriesMetadata,
|
|
271
|
+
instance
|
|
272
|
+
} = _getInstanceFromSegmentations(segmentations, displaySetService) ?? {};
|
|
273
|
+
if (seriesMetadata && instance) {
|
|
274
|
+
// An event is triggered after adding the instance and the displaySet is created
|
|
275
|
+
src.DicomMetadataStore.addSeriesMetadata([seriesMetadata], true);
|
|
276
|
+
src.DicomMetadataStore.addInstances([instance], true);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/actions/index.ts
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/commandsModule.ts
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
const LABELMAP = dist_esm.Enums.SegmentationRepresentations.Labelmap;
|
|
288
|
+
const commandsModule = ({
|
|
289
|
+
commandsManager,
|
|
290
|
+
servicesManager
|
|
291
|
+
}) => {
|
|
292
|
+
const services = servicesManager.services;
|
|
293
|
+
const {
|
|
294
|
+
displaySetService,
|
|
295
|
+
viewportGridService,
|
|
296
|
+
segmentationService
|
|
297
|
+
} = services;
|
|
298
|
+
const actions = {
|
|
299
|
+
...actions_namespaceObject,
|
|
300
|
+
getDynamic4DDisplaySet: () => {
|
|
301
|
+
const displaySets = displaySetService.getActiveDisplaySets();
|
|
302
|
+
const dynamic4DDisplaySet = displaySets.find(displaySet => {
|
|
303
|
+
const anInstance = displaySet.instances?.[0];
|
|
304
|
+
if (anInstance) {
|
|
305
|
+
return anInstance.FrameReferenceTime !== undefined || anInstance.NumberOfTimeSlices !== undefined || anInstance.TemporalPositionIdentifier !== undefined;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
});
|
|
309
|
+
return dynamic4DDisplaySet;
|
|
310
|
+
},
|
|
311
|
+
getComputedDisplaySets: () => {
|
|
312
|
+
const displaySetCache = displaySetService.getDisplaySetCache();
|
|
313
|
+
const cachedDisplaySets = [...displaySetCache.values()];
|
|
314
|
+
const computedDisplaySets = cachedDisplaySets.filter(displaySet => {
|
|
315
|
+
return displaySet.isDerived;
|
|
316
|
+
});
|
|
317
|
+
return computedDisplaySets;
|
|
318
|
+
},
|
|
319
|
+
exportTimeReportCSV: ({
|
|
320
|
+
segmentations,
|
|
321
|
+
config,
|
|
322
|
+
options,
|
|
323
|
+
summaryStats
|
|
324
|
+
}) => {
|
|
325
|
+
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
|
|
326
|
+
const volumeId = dynamic4DDisplaySet?.displaySetInstanceUID;
|
|
327
|
+
|
|
328
|
+
// cache._volumeCache is a map that has a key that includes the volumeId
|
|
329
|
+
// it is not exactly the volumeId, but it is the key that includes the volumeId
|
|
330
|
+
// so we can't do cache._volumeCache.get(volumeId) we should iterate
|
|
331
|
+
// over the keys and find the one that includes the volumeId
|
|
332
|
+
let volumeCacheKey;
|
|
333
|
+
for (const [key] of esm.cache._volumeCache) {
|
|
334
|
+
if (key.includes(volumeId)) {
|
|
335
|
+
volumeCacheKey = key;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
let dynamicVolume;
|
|
340
|
+
if (volumeCacheKey) {
|
|
341
|
+
dynamicVolume = esm.cache.getVolume(volumeCacheKey);
|
|
342
|
+
}
|
|
343
|
+
const instance = dynamic4DDisplaySet.instances[0];
|
|
344
|
+
const csv = [];
|
|
345
|
+
|
|
346
|
+
// CSV header information with placeholder empty values for the metadata lines
|
|
347
|
+
csv.push(`Patient ID,${instance.PatientID},`);
|
|
348
|
+
csv.push(`Study Date,${instance.StudyDate},`);
|
|
349
|
+
csv.push(`StudyInstanceUID,${instance.StudyInstanceUID},`);
|
|
350
|
+
csv.push(`StudyDescription,${instance.StudyDescription},`);
|
|
351
|
+
csv.push(`SeriesInstanceUID,${instance.SeriesInstanceUID},`);
|
|
352
|
+
|
|
353
|
+
// empty line
|
|
354
|
+
csv.push('');
|
|
355
|
+
csv.push('');
|
|
356
|
+
|
|
357
|
+
// Helper function to calculate standard deviation
|
|
358
|
+
function calculateStandardDeviation(data) {
|
|
359
|
+
const n = data.length;
|
|
360
|
+
const mean = data.reduce((acc, value) => acc + value, 0) / n;
|
|
361
|
+
const squaredDifferences = data.map(value => (value - mean) ** 2);
|
|
362
|
+
const variance = squaredDifferences.reduce((acc, value) => acc + value, 0) / n;
|
|
363
|
+
const stdDeviation = Math.sqrt(variance);
|
|
364
|
+
return stdDeviation;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Iterate through each segmentation to get the timeData and ijkCoords
|
|
368
|
+
segmentations.forEach((segmentation, segmentationIndex) => {
|
|
369
|
+
const [timeData, ijkCoords] = dist_esm.utilities.dynamicVolume.getDataInTime(dynamicVolume, {
|
|
370
|
+
maskVolumeId: segmentation.id
|
|
371
|
+
});
|
|
372
|
+
if (summaryStats) {
|
|
373
|
+
// Adding column headers for pixel identifier and segmentation label ids
|
|
374
|
+
let headers = 'Operation,Segmentation Label ID';
|
|
375
|
+
const maxLength = dynamicVolume.numTimePoints;
|
|
376
|
+
for (let t = 0; t < maxLength; t++) {
|
|
377
|
+
headers += `,Time Point ${t}`;
|
|
378
|
+
}
|
|
379
|
+
csv.push(headers);
|
|
380
|
+
// // perform summary statistics on the timeData including for each time point, mean, median, min, max, and standard deviation for
|
|
381
|
+
// // all the voxels in the ROI
|
|
382
|
+
const mean = [];
|
|
383
|
+
const min = [];
|
|
384
|
+
const minIJK = [];
|
|
385
|
+
const max = [];
|
|
386
|
+
const maxIJK = [];
|
|
387
|
+
const std = [];
|
|
388
|
+
const numVoxels = timeData.length;
|
|
389
|
+
// Helper function to calculate standard deviation
|
|
390
|
+
for (let timeIndex = 0; timeIndex < maxLength; timeIndex++) {
|
|
391
|
+
// for each voxel in the ROI, get the value at the current time point
|
|
392
|
+
const voxelValues = [];
|
|
393
|
+
for (let voxelIndex = 0; voxelIndex < numVoxels; voxelIndex++) {
|
|
394
|
+
voxelValues.push(timeData[voxelIndex][timeIndex]);
|
|
395
|
+
}
|
|
396
|
+
mean.push(voxelValues.reduce((acc, value) => acc + value, 0) / numVoxels);
|
|
397
|
+
const minimum = Math.min(...voxelValues);
|
|
398
|
+
min.push(minimum);
|
|
399
|
+
minIJK.push(ijkCoords[voxelValues.indexOf(minimum)]);
|
|
400
|
+
const maximum = Math.max(...voxelValues);
|
|
401
|
+
max.push(maximum);
|
|
402
|
+
maxIJK.push(ijkCoords[voxelValues.indexOf(maximum)]);
|
|
403
|
+
std.push(calculateStandardDeviation(voxelValues));
|
|
404
|
+
}
|
|
405
|
+
let row = `Mean,${segmentation.label}`;
|
|
406
|
+
// Generate separate rows for each statistic
|
|
407
|
+
for (let t = 0; t < maxLength; t++) {
|
|
408
|
+
row += `,${mean[t]}`;
|
|
409
|
+
}
|
|
410
|
+
csv.push(row);
|
|
411
|
+
row = `Standard Deviation,${segmentation.label}`;
|
|
412
|
+
for (let t = 0; t < maxLength; t++) {
|
|
413
|
+
row += `,${std[t]}`;
|
|
414
|
+
}
|
|
415
|
+
csv.push(row);
|
|
416
|
+
row = `Min,${segmentation.label}`;
|
|
417
|
+
for (let t = 0; t < maxLength; t++) {
|
|
418
|
+
row += `,${min[t]}`;
|
|
419
|
+
}
|
|
420
|
+
csv.push(row);
|
|
421
|
+
row = `Max,${segmentation.label}`;
|
|
422
|
+
for (let t = 0; t < maxLength; t++) {
|
|
423
|
+
row += `,${max[t]}`;
|
|
424
|
+
}
|
|
425
|
+
csv.push(row);
|
|
426
|
+
} else {
|
|
427
|
+
// Adding column headers for pixel identifier and segmentation label ids
|
|
428
|
+
let headers = 'Pixel Identifier (IJK),Segmentation Label ID';
|
|
429
|
+
const maxLength = dynamicVolume.numTimePoints;
|
|
430
|
+
for (let t = 0; t < maxLength; t++) {
|
|
431
|
+
headers += `,Time Point ${t}`;
|
|
432
|
+
}
|
|
433
|
+
csv.push(headers);
|
|
434
|
+
// Assuming timeData and ijkCoords are of the same length
|
|
435
|
+
for (let i = 0; i < timeData.length; i++) {
|
|
436
|
+
// Generate the pixel identifier
|
|
437
|
+
const pixelIdentifier = `${ijkCoords[i][0]}_${ijkCoords[i][1]}_${ijkCoords[i][2]}`;
|
|
438
|
+
|
|
439
|
+
// Start a new row for the current pixel
|
|
440
|
+
let row = `${pixelIdentifier},${segmentation.label}`;
|
|
441
|
+
|
|
442
|
+
// Add time data points for this pixel
|
|
443
|
+
for (let t = 0; t < timeData[i].length; t++) {
|
|
444
|
+
row += `,${timeData[i][t]}`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Append the row to the CSV array
|
|
448
|
+
csv.push(row);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Convert to CSV string
|
|
454
|
+
const csvContent = csv.join('\n');
|
|
455
|
+
|
|
456
|
+
// Generate filename and trigger download
|
|
457
|
+
const filename = `${instance.PatientID}.csv`;
|
|
458
|
+
const blob = new Blob([csvContent], {
|
|
459
|
+
type: 'text/csv;charset=utf-8;'
|
|
460
|
+
});
|
|
461
|
+
const link = document.createElement('a');
|
|
462
|
+
const url = URL.createObjectURL(blob);
|
|
463
|
+
link.setAttribute('href', url);
|
|
464
|
+
link.setAttribute('download', filename);
|
|
465
|
+
link.style.visibility = 'hidden';
|
|
466
|
+
document.body.appendChild(link);
|
|
467
|
+
link.click();
|
|
468
|
+
document.body.removeChild(link);
|
|
469
|
+
},
|
|
470
|
+
swapDynamicWithComputedDisplaySet: ({
|
|
471
|
+
displaySet
|
|
472
|
+
}) => {
|
|
473
|
+
const computedDisplaySet = displaySet;
|
|
474
|
+
const displaySetCache = displaySetService.getDisplaySetCache();
|
|
475
|
+
const cachedDisplaySetKeys = [displaySetCache.keys()];
|
|
476
|
+
const {
|
|
477
|
+
displaySetInstanceUID
|
|
478
|
+
} = computedDisplaySet;
|
|
479
|
+
// Check to see if computed display set is already in cache
|
|
480
|
+
if (!cachedDisplaySetKeys.includes(displaySetInstanceUID)) {
|
|
481
|
+
displaySetCache.set(displaySetInstanceUID, computedDisplaySet);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Get all viewports and their corresponding indices
|
|
485
|
+
const {
|
|
486
|
+
viewports
|
|
487
|
+
} = viewportGridService.getState();
|
|
488
|
+
|
|
489
|
+
// get the viewports in the grid
|
|
490
|
+
// iterate over them and find the ones that are showing a dynamic
|
|
491
|
+
// volume (displaySet), and replace that exact displaySet with the
|
|
492
|
+
// computed displaySet
|
|
493
|
+
|
|
494
|
+
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
|
|
495
|
+
const viewportsToUpdate = [];
|
|
496
|
+
for (const [key, value] of viewports) {
|
|
497
|
+
const viewport = value;
|
|
498
|
+
const viewportOptions = viewport.viewportOptions;
|
|
499
|
+
const {
|
|
500
|
+
displaySetInstanceUIDs
|
|
501
|
+
} = viewport;
|
|
502
|
+
const displaySetInstanceUIDIndex = displaySetInstanceUIDs.indexOf(dynamic4DDisplaySet.displaySetInstanceUID);
|
|
503
|
+
if (displaySetInstanceUIDIndex !== -1) {
|
|
504
|
+
const newViewport = {
|
|
505
|
+
viewportId: viewport.viewportId,
|
|
506
|
+
// merge the other displaySetInstanceUIDs with the new one
|
|
507
|
+
displaySetInstanceUIDs: [...displaySetInstanceUIDs.slice(0, displaySetInstanceUIDIndex), displaySetInstanceUID, ...displaySetInstanceUIDs.slice(displaySetInstanceUIDIndex + 1)],
|
|
508
|
+
viewportOptions: {
|
|
509
|
+
initialImageOptions: viewportOptions.initialImageOptions,
|
|
510
|
+
viewportType: 'volume',
|
|
511
|
+
orientation: viewportOptions.orientation,
|
|
512
|
+
background: viewportOptions.background
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
viewportsToUpdate.push(newViewport);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
viewportGridService.setDisplaySetsForViewports(viewportsToUpdate);
|
|
519
|
+
},
|
|
520
|
+
swapComputedWithDynamicDisplaySet: () => {
|
|
521
|
+
// Todo: this assumes there is only one dynamic display set in the viewer
|
|
522
|
+
const dynamicDisplaySet = actions.getDynamic4DDisplaySet();
|
|
523
|
+
const displaySetCache = displaySetService.getDisplaySetCache();
|
|
524
|
+
const cachedDisplaySetKeys = [...displaySetCache.keys()]; // Fix: Spread to get the array
|
|
525
|
+
const {
|
|
526
|
+
displaySetInstanceUID
|
|
527
|
+
} = dynamicDisplaySet;
|
|
528
|
+
|
|
529
|
+
// Check to see if dynamic display set is already in cache
|
|
530
|
+
if (!cachedDisplaySetKeys.includes(displaySetInstanceUID)) {
|
|
531
|
+
displaySetCache.set(displaySetInstanceUID, dynamicDisplaySet);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Get all viewports and their corresponding indices
|
|
535
|
+
const {
|
|
536
|
+
viewports
|
|
537
|
+
} = viewportGridService.getState();
|
|
538
|
+
|
|
539
|
+
// Get the computed 4D display set
|
|
540
|
+
const computed4DDisplaySet = actions.getComputedDisplaySets()[0];
|
|
541
|
+
const viewportsToUpdate = [];
|
|
542
|
+
for (const [key, value] of viewports) {
|
|
543
|
+
const viewport = value;
|
|
544
|
+
const viewportOptions = viewport.viewportOptions;
|
|
545
|
+
const {
|
|
546
|
+
displaySetInstanceUIDs
|
|
547
|
+
} = viewport;
|
|
548
|
+
const displaySetInstanceUIDIndex = displaySetInstanceUIDs.indexOf(computed4DDisplaySet.displaySetInstanceUID);
|
|
549
|
+
if (displaySetInstanceUIDIndex !== -1) {
|
|
550
|
+
const newViewport = {
|
|
551
|
+
viewportId: viewport.viewportId,
|
|
552
|
+
// merge the other displaySetInstanceUIDs with the new one
|
|
553
|
+
displaySetInstanceUIDs: [...displaySetInstanceUIDs.slice(0, displaySetInstanceUIDIndex), displaySetInstanceUID, ...displaySetInstanceUIDs.slice(displaySetInstanceUIDIndex + 1)],
|
|
554
|
+
viewportOptions: {
|
|
555
|
+
initialImageOptions: viewportOptions.initialImageOptions,
|
|
556
|
+
viewportType: 'volume',
|
|
557
|
+
orientation: viewportOptions.orientation,
|
|
558
|
+
background: viewportOptions.background
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
viewportsToUpdate.push(newViewport);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
viewportGridService.setDisplaySetsForViewports(viewportsToUpdate);
|
|
565
|
+
},
|
|
566
|
+
createNewLabelMapForDynamicVolume: async ({
|
|
567
|
+
label
|
|
568
|
+
}) => {
|
|
569
|
+
const {
|
|
570
|
+
viewports,
|
|
571
|
+
activeViewportId
|
|
572
|
+
} = viewportGridService.getState();
|
|
573
|
+
|
|
574
|
+
// get the dynamic 4D display set
|
|
575
|
+
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
|
|
576
|
+
const dynamic4DDisplaySetInstanceUID = dynamic4DDisplaySet.displaySetInstanceUID;
|
|
577
|
+
|
|
578
|
+
// check if the dynamic 4D display set is in the display, if not we might have
|
|
579
|
+
// the computed volumes and we should choose them for the segmentation
|
|
580
|
+
// creation
|
|
581
|
+
|
|
582
|
+
let referenceDisplaySet;
|
|
583
|
+
const activeViewport = viewports.get(activeViewportId);
|
|
584
|
+
const activeDisplaySetInstanceUIDs = activeViewport.displaySetInstanceUIDs;
|
|
585
|
+
const dynamicIsInActiveViewport = activeDisplaySetInstanceUIDs.includes(dynamic4DDisplaySetInstanceUID);
|
|
586
|
+
if (dynamicIsInActiveViewport) {
|
|
587
|
+
referenceDisplaySet = dynamic4DDisplaySet;
|
|
588
|
+
}
|
|
589
|
+
if (!referenceDisplaySet) {
|
|
590
|
+
// try to see if there is any derived displaySet in the active viewport
|
|
591
|
+
// which is referencing the dynamic 4D display set
|
|
592
|
+
|
|
593
|
+
// Todo: this is wrong but I don't have time to fix it now
|
|
594
|
+
const cachedDisplaySets = displaySetService.getDisplaySetCache();
|
|
595
|
+
for (const [key, displaySet] of cachedDisplaySets) {
|
|
596
|
+
if (displaySet.referenceDisplaySetUID === dynamic4DDisplaySetInstanceUID) {
|
|
597
|
+
referenceDisplaySet = displaySet;
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (!referenceDisplaySet) {
|
|
603
|
+
throw new Error('No reference display set found based on the dynamic data');
|
|
604
|
+
}
|
|
605
|
+
const segmentationId = await segmentationService.createSegmentationForDisplaySet(referenceDisplaySet.displaySetInstanceUID, {
|
|
606
|
+
label
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Add Segmentation to all toolGroupIds in the viewer
|
|
610
|
+
const toolGroupIds = Array.from(viewports.values(), viewport => viewport.viewportOptions.toolGroupId);
|
|
611
|
+
const representationType = LABELMAP;
|
|
612
|
+
for (const toolGroupId of toolGroupIds) {
|
|
613
|
+
const hydrateSegmentation = true;
|
|
614
|
+
await segmentationService.addSegmentationRepresentationToToolGroup(toolGroupId, segmentationId, hydrateSegmentation, representationType);
|
|
615
|
+
segmentationService.setActiveSegmentationForToolGroup(segmentationId, toolGroupId);
|
|
616
|
+
}
|
|
617
|
+
return segmentationId;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const definitions = {
|
|
621
|
+
updateSegmentationsChartDisplaySet: {
|
|
622
|
+
commandFn: actions.updateSegmentationsChartDisplaySet,
|
|
623
|
+
storeContexts: [],
|
|
624
|
+
options: {}
|
|
625
|
+
},
|
|
626
|
+
exportTimeReportCSV: {
|
|
627
|
+
commandFn: actions.exportTimeReportCSV,
|
|
628
|
+
storeContexts: [],
|
|
629
|
+
options: {}
|
|
630
|
+
},
|
|
631
|
+
swapDynamicWithComputedDisplaySet: {
|
|
632
|
+
commandFn: actions.swapDynamicWithComputedDisplaySet,
|
|
633
|
+
storeContexts: [],
|
|
634
|
+
options: {}
|
|
635
|
+
},
|
|
636
|
+
createNewLabelMapForDynamicVolume: {
|
|
637
|
+
commandFn: actions.createNewLabelMapForDynamicVolume,
|
|
638
|
+
storeContexts: [],
|
|
639
|
+
options: {}
|
|
640
|
+
},
|
|
641
|
+
swapComputedWithDynamicDisplaySet: {
|
|
642
|
+
commandFn: actions.swapComputedWithDynamicDisplaySet,
|
|
643
|
+
storeContexts: [],
|
|
644
|
+
options: {}
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
return {
|
|
648
|
+
actions,
|
|
649
|
+
definitions,
|
|
650
|
+
defaultContext: 'DYNAMIC-VOLUME:CORNERSTONE'
|
|
651
|
+
};
|
|
652
|
+
};
|
|
653
|
+
/* harmony default export */ const src_commandsModule = (commandsModule);
|
|
654
|
+
// EXTERNAL MODULE: ../../../node_modules/react/index.js
|
|
655
|
+
var react = __webpack_require__(41766);
|
|
656
|
+
// EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
|
|
657
|
+
var prop_types = __webpack_require__(11374);
|
|
658
|
+
var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
|
|
659
|
+
// EXTERNAL MODULE: ../../ui/src/index.js + 785 modules
|
|
660
|
+
var ui_src = __webpack_require__(5085);
|
|
661
|
+
// EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/streaming-image-volume-loader/dist/esm/index.js + 13 modules
|
|
662
|
+
var streaming_image_volume_loader_dist_esm = __webpack_require__(23722);
|
|
663
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/DynamicVolumeControls.tsx
|
|
664
|
+
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); }
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
const controlClassNames = {
|
|
669
|
+
sizeClassName: 'w-[58px] h-[28px]',
|
|
670
|
+
arrowsDirection: 'horizontal',
|
|
671
|
+
labelPosition: 'bottom'
|
|
672
|
+
};
|
|
673
|
+
const Header = ({
|
|
674
|
+
title,
|
|
675
|
+
tooltip
|
|
676
|
+
}) => /*#__PURE__*/react.createElement("div", {
|
|
677
|
+
className: "flex items-center space-x-1"
|
|
678
|
+
}, /*#__PURE__*/react.createElement(ui_src/* Tooltip */.m_, {
|
|
679
|
+
content: /*#__PURE__*/react.createElement("div", {
|
|
680
|
+
className: "text-white"
|
|
681
|
+
}, tooltip),
|
|
682
|
+
position: "bottom-left",
|
|
683
|
+
tight: true,
|
|
684
|
+
tooltipBoxClassName: "max-w-xs"
|
|
685
|
+
}, /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
|
|
686
|
+
name: "info-link",
|
|
687
|
+
className: "text-primary-active h-[14px] w-[14px]"
|
|
688
|
+
})), /*#__PURE__*/react.createElement("span", {
|
|
689
|
+
className: "text-aqua-pale text-[11px] uppercase"
|
|
690
|
+
}, title));
|
|
691
|
+
const DynamicVolumeControls = ({
|
|
692
|
+
isPlaying,
|
|
693
|
+
onPlayPauseChange,
|
|
694
|
+
// fps
|
|
695
|
+
fps,
|
|
696
|
+
onFpsChange,
|
|
697
|
+
minFps,
|
|
698
|
+
maxFps,
|
|
699
|
+
// Frames
|
|
700
|
+
currentFrameIndex,
|
|
701
|
+
onFrameChange,
|
|
702
|
+
framesLength,
|
|
703
|
+
onGenerate,
|
|
704
|
+
onDoubleRangeChange,
|
|
705
|
+
onDynamicClick
|
|
706
|
+
}) => {
|
|
707
|
+
const [computedView, setComputedView] = (0,react.useState)(false);
|
|
708
|
+
const [computeViewMode, setComputeViewMode] = (0,react.useState)(esm.Enums.DynamicOperatorType.SUM);
|
|
709
|
+
const [sliderRangeValues, setSliderRangeValues] = (0,react.useState)([framesLength / 4, framesLength / 2]);
|
|
710
|
+
(0,react.useEffect)(() => {
|
|
711
|
+
setSliderRangeValues([framesLength / 4, framesLength / 2]);
|
|
712
|
+
}, [framesLength]);
|
|
713
|
+
const handleSliderChange = newValues => {
|
|
714
|
+
onDoubleRangeChange(newValues);
|
|
715
|
+
if (newValues[0] === sliderRangeValues[0] && newValues[1] === sliderRangeValues[1]) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
setSliderRangeValues(newValues);
|
|
719
|
+
};
|
|
720
|
+
return /*#__PURE__*/react.createElement("div", {
|
|
721
|
+
className: "flex select-none flex-col"
|
|
722
|
+
}, /*#__PURE__*/react.createElement(ui_src/* PanelSection */.aU, {
|
|
723
|
+
title: "Controls",
|
|
724
|
+
childrenClassName: "space-y-4 pb-5 px-5"
|
|
725
|
+
}, /*#__PURE__*/react.createElement("div", {
|
|
726
|
+
className: "mt-2"
|
|
727
|
+
}, /*#__PURE__*/react.createElement(Header, {
|
|
728
|
+
title: "View",
|
|
729
|
+
tooltip: 'Select the view mode, 4D to view the dynamic volume or Computed to view the computed volume'
|
|
730
|
+
}), /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.e2, {
|
|
731
|
+
className: "mt-2 w-full"
|
|
732
|
+
}, /*#__PURE__*/react.createElement("button", {
|
|
733
|
+
className: "w-1/2",
|
|
734
|
+
onClick: () => {
|
|
735
|
+
setComputedView(false);
|
|
736
|
+
onDynamicClick?.();
|
|
737
|
+
}
|
|
738
|
+
}, "4D"), /*#__PURE__*/react.createElement("button", {
|
|
739
|
+
className: "w-1/2",
|
|
740
|
+
onClick: () => {
|
|
741
|
+
setComputedView(true);
|
|
742
|
+
}
|
|
743
|
+
}, "Computed"))), /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement(FrameControls, {
|
|
744
|
+
onPlayPauseChange: onPlayPauseChange,
|
|
745
|
+
isPlaying: isPlaying,
|
|
746
|
+
computedView: computedView
|
|
747
|
+
// fps
|
|
748
|
+
,
|
|
749
|
+
fps: fps,
|
|
750
|
+
onFpsChange: onFpsChange,
|
|
751
|
+
minFps: minFps,
|
|
752
|
+
maxFps: maxFps
|
|
753
|
+
//
|
|
754
|
+
,
|
|
755
|
+
framesLength: framesLength,
|
|
756
|
+
onFrameChange: onFrameChange,
|
|
757
|
+
currentFrameIndex: currentFrameIndex
|
|
758
|
+
})), /*#__PURE__*/react.createElement("div", {
|
|
759
|
+
className: `mt-6 flex flex-col ${computedView ? '' : 'ohif-disabled'}`
|
|
760
|
+
}, /*#__PURE__*/react.createElement(Header, {
|
|
761
|
+
title: "Computed Operation"
|
|
762
|
+
}), /*#__PURE__*/react.createElement(ui_src/* ButtonGroup */.e2, {
|
|
763
|
+
className: `mt-2 w-full `,
|
|
764
|
+
separated: true
|
|
765
|
+
}, /*#__PURE__*/react.createElement("button", {
|
|
766
|
+
className: "w-1/2",
|
|
767
|
+
onClick: () => setComputeViewMode(esm.Enums.DynamicOperatorType.SUM)
|
|
768
|
+
}, esm.Enums.DynamicOperatorType.SUM.toString().toUpperCase()), /*#__PURE__*/react.createElement("button", {
|
|
769
|
+
className: "w-1/2",
|
|
770
|
+
onClick: () => setComputeViewMode(esm.Enums.DynamicOperatorType.AVERAGE)
|
|
771
|
+
}, esm.Enums.DynamicOperatorType.AVERAGE.toString().toUpperCase()), /*#__PURE__*/react.createElement("button", {
|
|
772
|
+
className: "w-1/2",
|
|
773
|
+
onClick: () => setComputeViewMode(esm.Enums.DynamicOperatorType.SUBTRACT)
|
|
774
|
+
}, esm.Enums.DynamicOperatorType.SUBTRACT.toString().toUpperCase())), /*#__PURE__*/react.createElement("div", {
|
|
775
|
+
className: "w-ful mt-2"
|
|
776
|
+
}, /*#__PURE__*/react.createElement(ui_src/* InputDoubleRange */.Z5, {
|
|
777
|
+
values: sliderRangeValues,
|
|
778
|
+
onChange: handleSliderChange,
|
|
779
|
+
minValue: 0,
|
|
780
|
+
showLabel: true,
|
|
781
|
+
allowNumberEdit: true,
|
|
782
|
+
maxValue: framesLength,
|
|
783
|
+
step: 1
|
|
784
|
+
})), /*#__PURE__*/react.createElement(ui_src/* Button */.$n, {
|
|
785
|
+
className: "mt-2 !h-[26px] !w-[115px] self-start !p-0",
|
|
786
|
+
onClick: () => {
|
|
787
|
+
onGenerate(computeViewMode);
|
|
788
|
+
}
|
|
789
|
+
}, "Generate"))));
|
|
790
|
+
};
|
|
791
|
+
/* harmony default export */ const panels_DynamicVolumeControls = (DynamicVolumeControls);
|
|
792
|
+
function FrameControls({
|
|
793
|
+
isPlaying,
|
|
794
|
+
onPlayPauseChange,
|
|
795
|
+
fps,
|
|
796
|
+
minFps,
|
|
797
|
+
maxFps,
|
|
798
|
+
onFpsChange,
|
|
799
|
+
framesLength,
|
|
800
|
+
onFrameChange,
|
|
801
|
+
currentFrameIndex,
|
|
802
|
+
computedView
|
|
803
|
+
}) {
|
|
804
|
+
const getPlayPauseIconName = () => isPlaying ? 'icon-pause' : 'icon-play';
|
|
805
|
+
return /*#__PURE__*/react.createElement("div", {
|
|
806
|
+
className: computedView && 'ohif-disabled'
|
|
807
|
+
}, /*#__PURE__*/react.createElement(Header, {
|
|
808
|
+
title: "4D Controls"
|
|
809
|
+
}), /*#__PURE__*/react.createElement("div", {
|
|
810
|
+
className: "mt-3 flex justify-between"
|
|
811
|
+
}, /*#__PURE__*/react.createElement(ui_src/* IconButton */.K0, {
|
|
812
|
+
className: "bg-customblue-30 h-[26px] w-[58px] rounded-[4px]",
|
|
813
|
+
onClick: () => onPlayPauseChange(!isPlaying)
|
|
814
|
+
}, /*#__PURE__*/react.createElement(ui_src/* Icon */.In, {
|
|
815
|
+
name: getPlayPauseIconName(),
|
|
816
|
+
className: " active:text-primary-light hover:bg-customblue-300 h-[24px] w-[24px] cursor-pointer text-white"
|
|
817
|
+
})), /*#__PURE__*/react.createElement(ui_src/* InputNumber */.YI, _extends({
|
|
818
|
+
value: currentFrameIndex,
|
|
819
|
+
onChange: onFrameChange,
|
|
820
|
+
minValue: 0,
|
|
821
|
+
maxValue: framesLength,
|
|
822
|
+
label: "Frame"
|
|
823
|
+
}, controlClassNames)), /*#__PURE__*/react.createElement(ui_src/* InputNumber */.YI, _extends({
|
|
824
|
+
value: fps,
|
|
825
|
+
onChange: onFpsChange,
|
|
826
|
+
minValue: minFps,
|
|
827
|
+
maxValue: maxFps
|
|
828
|
+
}, controlClassNames, {
|
|
829
|
+
label: "FPS"
|
|
830
|
+
}))));
|
|
831
|
+
}
|
|
832
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/PanelGenerateImage.tsx
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
const SOPClassHandlerId = '@ohif/extension-default.sopClassHandlerModule.stack';
|
|
841
|
+
function PanelGenerateImage({
|
|
842
|
+
servicesManager,
|
|
843
|
+
commandsManager
|
|
844
|
+
}) {
|
|
845
|
+
const {
|
|
846
|
+
cornerstoneViewportService,
|
|
847
|
+
viewportGridService,
|
|
848
|
+
displaySetService
|
|
849
|
+
} = servicesManager.services;
|
|
850
|
+
const [{
|
|
851
|
+
isCineEnabled
|
|
852
|
+
}, cineService] = (0,ui_src/* useCine */.tq)();
|
|
853
|
+
const [{
|
|
854
|
+
activeViewportId
|
|
855
|
+
}] = (0,ui_src/* useViewportGrid */.ih)();
|
|
856
|
+
|
|
857
|
+
//
|
|
858
|
+
const [timePointsRange, setTimePointsRange] = (0,react.useState)([]);
|
|
859
|
+
const [timePointsRangeToUseForGenerate, setTimePointsRangeToUseForGenerate] = (0,react.useState)([]);
|
|
860
|
+
const [computedDisplaySet, setComputedDisplaySet] = (0,react.useState)(null);
|
|
861
|
+
const [dynamicVolume, setDynamicVolume] = (0,react.useState)(null);
|
|
862
|
+
const [frameRate, setFrameRate] = (0,react.useState)(20);
|
|
863
|
+
const [isPlaying, setIsPlaying] = (0,react.useState)(isCineEnabled);
|
|
864
|
+
const [timePointRendered, setTimePointRendered] = (0,react.useState)(null);
|
|
865
|
+
const [displayingComputed, setDisplayingComputed] = (0,react.useState)(false);
|
|
866
|
+
|
|
867
|
+
//
|
|
868
|
+
const uuidComputedVolume = (0,react.useRef)(esm.utilities.uuidv4());
|
|
869
|
+
const uuidDynamicVolume = (0,react.useRef)(null);
|
|
870
|
+
const computedVolumeId = `cornerstoneStreamingImageVolume:${uuidComputedVolume.current}`;
|
|
871
|
+
(0,react.useEffect)(() => {
|
|
872
|
+
const evt = cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED;
|
|
873
|
+
const {
|
|
874
|
+
unsubscribe
|
|
875
|
+
} = cornerstoneViewportService.subscribe(evt, evtDetails => {
|
|
876
|
+
evtDetails.viewportData.data.forEach(volumeData => {
|
|
877
|
+
if (volumeData.volume.isDynamicVolume()) {
|
|
878
|
+
setDynamicVolume(volumeData.volume);
|
|
879
|
+
uuidDynamicVolume.current = volumeData.displaySetInstanceUID;
|
|
880
|
+
setTimePointsRange([1, volumeData.volume.numTimePoints]);
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
return () => {
|
|
885
|
+
unsubscribe();
|
|
886
|
+
};
|
|
887
|
+
}, [cornerstoneViewportService]);
|
|
888
|
+
(0,react.useEffect)(() => {
|
|
889
|
+
const {
|
|
890
|
+
unsubscribe
|
|
891
|
+
} = servicesManager.services.cineService.subscribe(servicesManager.services.cineService.EVENTS.CINE_STATE_CHANGED, evt => {
|
|
892
|
+
setIsPlaying(evt.isPlaying);
|
|
893
|
+
});
|
|
894
|
+
return () => {
|
|
895
|
+
unsubscribe();
|
|
896
|
+
};
|
|
897
|
+
}, [cineService]);
|
|
898
|
+
(0,react.useEffect)(() => {
|
|
899
|
+
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(activeViewportId);
|
|
900
|
+
if (!displaySetUIDs || displaySetUIDs.length === 0) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const displaySets = displaySetUIDs.map(displaySetUID => displaySetService.getDisplaySetByUID(displaySetUID));
|
|
904
|
+
const dynamicVolumeDisplaySet = displaySets.find(displaySet => displaySet.isDynamicVolume);
|
|
905
|
+
if (!dynamicVolumeDisplaySet) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const dynamicVolume = esm.cache.getVolumes().find(volume => volume.volumeId.includes(dynamicVolumeDisplaySet.displaySetInstanceUID));
|
|
909
|
+
if (!dynamicVolume) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
setDynamicVolume(dynamicVolume);
|
|
913
|
+
uuidDynamicVolume.current = dynamicVolumeDisplaySet.displaySetInstanceUID;
|
|
914
|
+
setTimePointsRange([1, dynamicVolume.numTimePoints]);
|
|
915
|
+
}, [activeViewportId, cornerstoneViewportService]);
|
|
916
|
+
(0,react.useEffect)(() => {
|
|
917
|
+
// ~~ Subscription
|
|
918
|
+
const evt = streaming_image_volume_loader_dist_esm/* Enums.Events */.fX.s.DYNAMIC_VOLUME_TIME_POINT_INDEX_CHANGED;
|
|
919
|
+
const callback = evt => {
|
|
920
|
+
setTimePointRendered(evt.detail.timePointIndex);
|
|
921
|
+
};
|
|
922
|
+
esm.eventTarget.addEventListener(evt, callback);
|
|
923
|
+
return () => {
|
|
924
|
+
esm.eventTarget.removeEventListener(evt, callback);
|
|
925
|
+
};
|
|
926
|
+
}, [cornerstoneViewportService]);
|
|
927
|
+
function renderGeneratedImage(displaySet) {
|
|
928
|
+
commandsManager.runCommand('swapDynamicWithComputedDisplaySet', {
|
|
929
|
+
displaySet
|
|
930
|
+
});
|
|
931
|
+
setDisplayingComputed(true);
|
|
932
|
+
}
|
|
933
|
+
function renderDynamicImage(displaySet) {
|
|
934
|
+
commandsManager.runCommand('swapComputedWithDynamicDisplaySet');
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Get computed volume from cache, calculate the data across the time frames,
|
|
938
|
+
// set the scalar data to the computedVolume, and create displaySet
|
|
939
|
+
async function onGenerateImage(operationName) {
|
|
940
|
+
const dynamicVolumeId = dynamicVolume.volumeId;
|
|
941
|
+
if (!dynamicVolumeId) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
let computedVolume = esm.cache.getVolume(computedVolumeId);
|
|
945
|
+
if (!computedVolume) {
|
|
946
|
+
await createComputedVolume(dynamicVolumeId, computedVolumeId);
|
|
947
|
+
computedVolume = esm.cache.getVolume(computedVolumeId);
|
|
948
|
+
}
|
|
949
|
+
const vals = timePointsRangeToUseForGenerate;
|
|
950
|
+
const targets = Array.from({
|
|
951
|
+
length: vals[1] - vals[0] + 1
|
|
952
|
+
}, (_, i) => i + vals[0]);
|
|
953
|
+
const dataInTime = dist_esm.utilities.dynamicVolume.generateImageFromTimeData(dynamicVolume, operationName, operationName === 'SUBTRACT' ? vals : targets);
|
|
954
|
+
|
|
955
|
+
// Add loadStatus.loaded to computed volume and set to true
|
|
956
|
+
computedVolume.loadStatus = {};
|
|
957
|
+
computedVolume.loadStatus.loaded = true;
|
|
958
|
+
// Set computed scalar data to volume
|
|
959
|
+
const scalarData = computedVolume.getScalarData();
|
|
960
|
+
scalarData.set(dataInTime);
|
|
961
|
+
|
|
962
|
+
// If computed display set does not exist, create an object to be used as
|
|
963
|
+
// the displaySet. If it does exist, update the image data and vtkTexture
|
|
964
|
+
if (!computedDisplaySet) {
|
|
965
|
+
const displaySet = {
|
|
966
|
+
volumeLoaderSchema: computedVolume.volumeId.split(':')[0],
|
|
967
|
+
displaySetInstanceUID: uuidComputedVolume.current,
|
|
968
|
+
SOPClassHandlerId: SOPClassHandlerId,
|
|
969
|
+
Modality: dynamicVolume.metadata.Modality,
|
|
970
|
+
isMultiFrame: false,
|
|
971
|
+
numImageFrames: 1,
|
|
972
|
+
uid: uuidComputedVolume.current,
|
|
973
|
+
referenceDisplaySetUID: dynamicVolume.volumeId.split(':')[1],
|
|
974
|
+
madeInClient: true,
|
|
975
|
+
FrameOfReferenceUID: dynamicVolume.metadata.FrameOfReferenceUID,
|
|
976
|
+
isDerived: true
|
|
977
|
+
};
|
|
978
|
+
setComputedDisplaySet(displaySet);
|
|
979
|
+
renderGeneratedImage(displaySet);
|
|
980
|
+
} else {
|
|
981
|
+
commandsManager.runCommand('updateVolumeData', {
|
|
982
|
+
volume: computedVolume
|
|
983
|
+
});
|
|
984
|
+
// Check if viewport is currently displaying the computed volume, if so,
|
|
985
|
+
// call render on the viewports to update the image, if not, call
|
|
986
|
+
// renderGeneratedImage
|
|
987
|
+
// if (!cache.getVolume(dynamicVolumeId)) {
|
|
988
|
+
// for (const viewportId of viewports.keys()) {
|
|
989
|
+
// const viewportForRendering =
|
|
990
|
+
// cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
|
991
|
+
// viewportForRendering.render();
|
|
992
|
+
// }
|
|
993
|
+
// } else {
|
|
994
|
+
cornerstoneViewportService.getRenderingEngine().render();
|
|
995
|
+
renderGeneratedImage(computedDisplaySet);
|
|
996
|
+
// }
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const onPlayPauseChange = isPlaying => {
|
|
1000
|
+
isPlaying ? handlePlay() : handleStop();
|
|
1001
|
+
};
|
|
1002
|
+
const handlePlay = () => {
|
|
1003
|
+
setIsPlaying(true);
|
|
1004
|
+
const viewportInfo = cornerstoneViewportService.getViewportInfo(activeViewportId);
|
|
1005
|
+
if (!viewportInfo) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const {
|
|
1009
|
+
element
|
|
1010
|
+
} = viewportInfo;
|
|
1011
|
+
cineService.playClip(element, {
|
|
1012
|
+
framesPerSecond: frameRate,
|
|
1013
|
+
viewportId: activeViewportId
|
|
1014
|
+
});
|
|
1015
|
+
};
|
|
1016
|
+
const handleStop = () => {
|
|
1017
|
+
setIsPlaying(false);
|
|
1018
|
+
const {
|
|
1019
|
+
element
|
|
1020
|
+
} = cornerstoneViewportService.getViewportInfo(activeViewportId);
|
|
1021
|
+
cineService.stopClip(element);
|
|
1022
|
+
};
|
|
1023
|
+
const handleSetFrameRate = newFrameRate => {
|
|
1024
|
+
setFrameRate(newFrameRate);
|
|
1025
|
+
handleStop();
|
|
1026
|
+
handlePlay();
|
|
1027
|
+
};
|
|
1028
|
+
function handleSliderChange(newValues) {
|
|
1029
|
+
if (newValues[0] === timePointsRangeToUseForGenerate[0] && newValues[1] === timePointsRangeToUseForGenerate[1]) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
setTimePointsRangeToUseForGenerate(newValues);
|
|
1033
|
+
}
|
|
1034
|
+
if (!dynamicVolume || timePointsRange.length === 0) {
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
return /*#__PURE__*/react.createElement(panels_DynamicVolumeControls, {
|
|
1038
|
+
fps: frameRate,
|
|
1039
|
+
isPlaying: isPlaying,
|
|
1040
|
+
onPlayPauseChange: onPlayPauseChange,
|
|
1041
|
+
minFps: 1,
|
|
1042
|
+
maxFps: 50,
|
|
1043
|
+
currentFrameIndex: timePointRendered,
|
|
1044
|
+
onFpsChange: handleSetFrameRate,
|
|
1045
|
+
framesLength: timePointsRange[1],
|
|
1046
|
+
onFrameChange: timePointIndex => {
|
|
1047
|
+
dynamicVolume.timePointIndex = timePointIndex;
|
|
1048
|
+
},
|
|
1049
|
+
onGenerate: onGenerateImage,
|
|
1050
|
+
onDynamicClick: displayingComputed ? () => renderDynamicImage(computedDisplaySet) : null,
|
|
1051
|
+
onDoubleRangeChange: handleSliderChange
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
async function createComputedVolume(dynamicVolumeId, computedVolumeId) {
|
|
1055
|
+
if (!esm.cache.getVolume(computedVolumeId)) {
|
|
1056
|
+
const computedVolume = await esm.volumeLoader.createAndCacheDerivedVolume(dynamicVolumeId, {
|
|
1057
|
+
volumeId: computedVolumeId
|
|
1058
|
+
});
|
|
1059
|
+
return computedVolume;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
PanelGenerateImage.propTypes = {
|
|
1063
|
+
servicesManager: prop_types_default().shape({
|
|
1064
|
+
services: prop_types_default().shape({
|
|
1065
|
+
measurementService: prop_types_default().shape({
|
|
1066
|
+
getMeasurements: (prop_types_default()).func.isRequired,
|
|
1067
|
+
subscribe: (prop_types_default()).func.isRequired,
|
|
1068
|
+
EVENTS: (prop_types_default()).object.isRequired,
|
|
1069
|
+
VALUE_TYPES: (prop_types_default()).object.isRequired
|
|
1070
|
+
}).isRequired
|
|
1071
|
+
}).isRequired
|
|
1072
|
+
}).isRequired
|
|
1073
|
+
};
|
|
1074
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/DynamicDataPanel.tsx
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
function DynamicDataPanel({
|
|
1078
|
+
servicesManager,
|
|
1079
|
+
commandsManager
|
|
1080
|
+
}) {
|
|
1081
|
+
return /*#__PURE__*/react.createElement("div", {
|
|
1082
|
+
className: "flex flex-auto flex-col text-white",
|
|
1083
|
+
"data-cy": 'dynamic-volume-panel'
|
|
1084
|
+
}, /*#__PURE__*/react.createElement(PanelGenerateImage, {
|
|
1085
|
+
commandsManager: commandsManager,
|
|
1086
|
+
servicesManager: servicesManager
|
|
1087
|
+
}));
|
|
1088
|
+
}
|
|
1089
|
+
/* harmony default export */ const panels_DynamicDataPanel = (DynamicDataPanel);
|
|
1090
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/WorkflowPanel.tsx
|
|
1091
|
+
|
|
1092
|
+
function WorkflowPanel({
|
|
1093
|
+
servicesManager,
|
|
1094
|
+
extensionManager
|
|
1095
|
+
}) {
|
|
1096
|
+
const ProgressDropdownWithService = useMemo(() => {
|
|
1097
|
+
const defaultComponents = extensionManager.getModuleEntry('@ohif/extension-default.customizationModule.default').value;
|
|
1098
|
+
return defaultComponents.find(component => component.id === 'progressDropdownWithServiceComponent').component;
|
|
1099
|
+
}, [extensionManager]);
|
|
1100
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1101
|
+
"data-cy": 'workflow-panel',
|
|
1102
|
+
className: "bg-secondary-dark mb-1 px-3 py-4"
|
|
1103
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1104
|
+
className: "mb-1"
|
|
1105
|
+
}, "Workflow"), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(ProgressDropdownWithService, {
|
|
1106
|
+
servicesManager: servicesManager
|
|
1107
|
+
})));
|
|
1108
|
+
}
|
|
1109
|
+
/* harmony default export */ const panels_WorkflowPanel = ((/* unused pure expression or super */ null && (WorkflowPanel)));
|
|
1110
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/index.js
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
// EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
|
|
1116
|
+
var es = __webpack_require__(80619);
|
|
1117
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/panels/DynamicExport.tsx
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
function DynamicExport({
|
|
1122
|
+
commandsManager,
|
|
1123
|
+
servicesManager,
|
|
1124
|
+
extensionManager
|
|
1125
|
+
}) {
|
|
1126
|
+
const {
|
|
1127
|
+
segmentationService
|
|
1128
|
+
} = servicesManager.services;
|
|
1129
|
+
const {
|
|
1130
|
+
t
|
|
1131
|
+
} = (0,es/* useTranslation */.Bd)('dynamicExport');
|
|
1132
|
+
const [segmentations, setSegmentations] = (0,react.useState)(() => segmentationService.getSegmentations());
|
|
1133
|
+
const actions = [{
|
|
1134
|
+
label: 'Export Time Data',
|
|
1135
|
+
onClick: () => {
|
|
1136
|
+
commandsManager.runCommand('exportTimeReportCSV', {
|
|
1137
|
+
segmentations,
|
|
1138
|
+
options: {
|
|
1139
|
+
filename: 'TimeData.csv'
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
},
|
|
1143
|
+
disabled: !segmentations?.length
|
|
1144
|
+
}, {
|
|
1145
|
+
label: 'Export ROI Stats',
|
|
1146
|
+
onClick: () => {
|
|
1147
|
+
commandsManager.runCommand('exportTimeReportCSV', {
|
|
1148
|
+
segmentations,
|
|
1149
|
+
summaryStats: true,
|
|
1150
|
+
options: {
|
|
1151
|
+
filename: 'ROIStats.csv'
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
},
|
|
1155
|
+
disabled: !segmentations?.length
|
|
1156
|
+
}];
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Update UI based on segmentation changes (added, removed, updated)
|
|
1160
|
+
*/
|
|
1161
|
+
(0,react.useEffect)(() => {
|
|
1162
|
+
// ~~ Subscription
|
|
1163
|
+
const added = segmentationService.EVENTS.SEGMENTATION_ADDED;
|
|
1164
|
+
const updated = segmentationService.EVENTS.SEGMENTATION_UPDATED;
|
|
1165
|
+
const removed = segmentationService.EVENTS.SEGMENTATION_REMOVED;
|
|
1166
|
+
const subscriptions = [];
|
|
1167
|
+
[added, updated, removed].forEach(evt => {
|
|
1168
|
+
const {
|
|
1169
|
+
unsubscribe
|
|
1170
|
+
} = segmentationService.subscribe(evt, () => {
|
|
1171
|
+
const segmentations = segmentationService.getSegmentations();
|
|
1172
|
+
setSegmentations(segmentations);
|
|
1173
|
+
});
|
|
1174
|
+
subscriptions.push(unsubscribe);
|
|
1175
|
+
});
|
|
1176
|
+
return () => {
|
|
1177
|
+
subscriptions.forEach(unsub => {
|
|
1178
|
+
unsub();
|
|
1179
|
+
});
|
|
1180
|
+
};
|
|
1181
|
+
}, []);
|
|
1182
|
+
return /*#__PURE__*/react.createElement("div", null, /*#__PURE__*/react.createElement("div", {
|
|
1183
|
+
className: "mt-3 flex justify-center px-2"
|
|
1184
|
+
}, /*#__PURE__*/react.createElement(ui_src/* ActionButtons */.wr, {
|
|
1185
|
+
actions: actions,
|
|
1186
|
+
t: t
|
|
1187
|
+
})));
|
|
1188
|
+
}
|
|
1189
|
+
/* harmony default export */ const panels_DynamicExport = (DynamicExport);
|
|
1190
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/getPanelModule.tsx
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
function getPanelModule({
|
|
1196
|
+
commandsManager,
|
|
1197
|
+
extensionManager,
|
|
1198
|
+
servicesManager
|
|
1199
|
+
}) {
|
|
1200
|
+
const wrappedDynamicDataPanel = () => {
|
|
1201
|
+
return /*#__PURE__*/react.createElement(panels_DynamicDataPanel, {
|
|
1202
|
+
commandsManager: commandsManager,
|
|
1203
|
+
servicesManager: servicesManager,
|
|
1204
|
+
extensionManager: extensionManager
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
const wrappedDynamicToolbox = () => {
|
|
1208
|
+
return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_src/* Toolbox */.OO, {
|
|
1209
|
+
commandsManager: commandsManager,
|
|
1210
|
+
servicesManager: servicesManager,
|
|
1211
|
+
extensionManager: extensionManager,
|
|
1212
|
+
buttonSectionId: "dynamic-toolbox",
|
|
1213
|
+
title: "Threshold Tools"
|
|
1214
|
+
}));
|
|
1215
|
+
};
|
|
1216
|
+
const wrappedDynamicExport = () => {
|
|
1217
|
+
return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(panels_DynamicExport, {
|
|
1218
|
+
commandsManager: commandsManager,
|
|
1219
|
+
servicesManager: servicesManager,
|
|
1220
|
+
extensionManager: extensionManager
|
|
1221
|
+
}));
|
|
1222
|
+
};
|
|
1223
|
+
return [{
|
|
1224
|
+
name: 'dynamic-volume',
|
|
1225
|
+
iconName: 'group-layers',
|
|
1226
|
+
iconLabel: '4D Workflow',
|
|
1227
|
+
label: '4D Workflow',
|
|
1228
|
+
component: wrappedDynamicDataPanel
|
|
1229
|
+
}, {
|
|
1230
|
+
name: 'dynamic-toolbox',
|
|
1231
|
+
iconName: 'group-layers',
|
|
1232
|
+
iconLabel: '4D Workflow',
|
|
1233
|
+
label: 'Dynamic Toolbox',
|
|
1234
|
+
component: wrappedDynamicToolbox
|
|
1235
|
+
}, {
|
|
1236
|
+
name: 'dynamic-export',
|
|
1237
|
+
iconName: 'group-layers',
|
|
1238
|
+
iconLabel: '4D Workflow',
|
|
1239
|
+
label: '4D Workflow',
|
|
1240
|
+
component: wrappedDynamicExport
|
|
1241
|
+
}];
|
|
1242
|
+
}
|
|
1243
|
+
/* harmony default export */ const src_getPanelModule = (getPanelModule);
|
|
1244
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/getHangingProtocolModule.ts
|
|
1245
|
+
const DEFAULT_COLORMAP = '2hot';
|
|
1246
|
+
const toolGroupIds = {
|
|
1247
|
+
pt: 'dynamic4D-pt',
|
|
1248
|
+
fusion: 'dynamic4D-fusion',
|
|
1249
|
+
ct: 'dynamic4D-ct'
|
|
1250
|
+
};
|
|
1251
|
+
function getPTOptions({
|
|
1252
|
+
colormap,
|
|
1253
|
+
voiInverted
|
|
1254
|
+
} = {}) {
|
|
1255
|
+
return {
|
|
1256
|
+
blendMode: 'MIP',
|
|
1257
|
+
colormap,
|
|
1258
|
+
voi: {
|
|
1259
|
+
windowWidth: 5,
|
|
1260
|
+
windowCenter: 2.5
|
|
1261
|
+
},
|
|
1262
|
+
voiInverted
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function getPTViewports() {
|
|
1266
|
+
const ptOptionsParams = {
|
|
1267
|
+
colormap: {
|
|
1268
|
+
name: DEFAULT_COLORMAP,
|
|
1269
|
+
opacity: [{
|
|
1270
|
+
value: 0,
|
|
1271
|
+
opacity: 0
|
|
1272
|
+
}, {
|
|
1273
|
+
value: 0.1,
|
|
1274
|
+
opacity: 1
|
|
1275
|
+
}, {
|
|
1276
|
+
value: 1,
|
|
1277
|
+
opacity: 1
|
|
1278
|
+
}]
|
|
1279
|
+
},
|
|
1280
|
+
voiInverted: false
|
|
1281
|
+
};
|
|
1282
|
+
return [{
|
|
1283
|
+
viewportOptions: {
|
|
1284
|
+
viewportId: 'ptAxial',
|
|
1285
|
+
viewportType: 'volume',
|
|
1286
|
+
orientation: 'axial',
|
|
1287
|
+
toolGroupId: toolGroupIds.pt,
|
|
1288
|
+
initialImageOptions: {
|
|
1289
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1290
|
+
},
|
|
1291
|
+
syncGroups: [{
|
|
1292
|
+
type: 'cameraPosition',
|
|
1293
|
+
id: 'axialSync',
|
|
1294
|
+
source: true,
|
|
1295
|
+
target: true
|
|
1296
|
+
}, {
|
|
1297
|
+
type: 'voi',
|
|
1298
|
+
id: 'ptWLSync',
|
|
1299
|
+
source: true,
|
|
1300
|
+
target: true
|
|
1301
|
+
}]
|
|
1302
|
+
},
|
|
1303
|
+
displaySets: [{
|
|
1304
|
+
id: 'ptDisplaySet',
|
|
1305
|
+
options: {
|
|
1306
|
+
...getPTOptions(ptOptionsParams)
|
|
1307
|
+
}
|
|
1308
|
+
}]
|
|
1309
|
+
}, {
|
|
1310
|
+
viewportOptions: {
|
|
1311
|
+
viewportId: 'ptSagittal',
|
|
1312
|
+
viewportType: 'volume',
|
|
1313
|
+
orientation: 'sagittal',
|
|
1314
|
+
toolGroupId: toolGroupIds.pt,
|
|
1315
|
+
initialImageOptions: {
|
|
1316
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1317
|
+
},
|
|
1318
|
+
syncGroups: [{
|
|
1319
|
+
type: 'cameraPosition',
|
|
1320
|
+
id: 'sagittalSync',
|
|
1321
|
+
source: true,
|
|
1322
|
+
target: true
|
|
1323
|
+
}, {
|
|
1324
|
+
type: 'voi',
|
|
1325
|
+
id: 'ptWLSync',
|
|
1326
|
+
source: true,
|
|
1327
|
+
target: true
|
|
1328
|
+
}]
|
|
1329
|
+
},
|
|
1330
|
+
displaySets: [{
|
|
1331
|
+
id: 'ptDisplaySet',
|
|
1332
|
+
options: {
|
|
1333
|
+
...getPTOptions(ptOptionsParams)
|
|
1334
|
+
}
|
|
1335
|
+
}]
|
|
1336
|
+
}, {
|
|
1337
|
+
viewportOptions: {
|
|
1338
|
+
viewportId: 'ptCoronal',
|
|
1339
|
+
viewportType: 'volume',
|
|
1340
|
+
orientation: 'coronal',
|
|
1341
|
+
toolGroupId: toolGroupIds.pt,
|
|
1342
|
+
initialImageOptions: {
|
|
1343
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1344
|
+
},
|
|
1345
|
+
syncGroups: [{
|
|
1346
|
+
type: 'cameraPosition',
|
|
1347
|
+
id: 'coronalSync',
|
|
1348
|
+
source: true,
|
|
1349
|
+
target: true
|
|
1350
|
+
}, {
|
|
1351
|
+
type: 'voi',
|
|
1352
|
+
id: 'ptWLSync',
|
|
1353
|
+
source: true,
|
|
1354
|
+
target: true
|
|
1355
|
+
}]
|
|
1356
|
+
},
|
|
1357
|
+
displaySets: [{
|
|
1358
|
+
id: 'ptDisplaySet',
|
|
1359
|
+
options: {
|
|
1360
|
+
...getPTOptions(ptOptionsParams)
|
|
1361
|
+
}
|
|
1362
|
+
}]
|
|
1363
|
+
}];
|
|
1364
|
+
}
|
|
1365
|
+
function getFusionViewports() {
|
|
1366
|
+
const ptOptionsParams = {
|
|
1367
|
+
colormap: {
|
|
1368
|
+
name: DEFAULT_COLORMAP,
|
|
1369
|
+
opacity: [{
|
|
1370
|
+
value: 0,
|
|
1371
|
+
opacity: 0
|
|
1372
|
+
}, {
|
|
1373
|
+
value: 0.1,
|
|
1374
|
+
opacity: 0.3
|
|
1375
|
+
}, {
|
|
1376
|
+
value: 1,
|
|
1377
|
+
opacity: 0.3
|
|
1378
|
+
}]
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
return [{
|
|
1382
|
+
viewportOptions: {
|
|
1383
|
+
viewportId: 'fusionAxial',
|
|
1384
|
+
viewportType: 'volume',
|
|
1385
|
+
orientation: 'axial',
|
|
1386
|
+
toolGroupId: toolGroupIds.fusion,
|
|
1387
|
+
initialImageOptions: {
|
|
1388
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1389
|
+
},
|
|
1390
|
+
syncGroups: [{
|
|
1391
|
+
type: 'cameraPosition',
|
|
1392
|
+
id: 'axialSync',
|
|
1393
|
+
source: true,
|
|
1394
|
+
target: true
|
|
1395
|
+
}, {
|
|
1396
|
+
type: 'voi',
|
|
1397
|
+
id: 'ctWLSync',
|
|
1398
|
+
source: false,
|
|
1399
|
+
target: true
|
|
1400
|
+
}, {
|
|
1401
|
+
type: 'voi',
|
|
1402
|
+
id: 'fusionWLSync',
|
|
1403
|
+
source: true,
|
|
1404
|
+
target: true
|
|
1405
|
+
}, {
|
|
1406
|
+
type: 'voi',
|
|
1407
|
+
id: 'ptFusionWLSync',
|
|
1408
|
+
source: false,
|
|
1409
|
+
target: true,
|
|
1410
|
+
options: {
|
|
1411
|
+
syncInvertState: false
|
|
1412
|
+
}
|
|
1413
|
+
}]
|
|
1414
|
+
},
|
|
1415
|
+
displaySets: [{
|
|
1416
|
+
id: 'ctDisplaySet'
|
|
1417
|
+
}, {
|
|
1418
|
+
options: {
|
|
1419
|
+
...getPTOptions(ptOptionsParams)
|
|
1420
|
+
},
|
|
1421
|
+
id: 'ptDisplaySet'
|
|
1422
|
+
}]
|
|
1423
|
+
}, {
|
|
1424
|
+
viewportOptions: {
|
|
1425
|
+
viewportId: 'fusionSagittal',
|
|
1426
|
+
viewportType: 'volume',
|
|
1427
|
+
orientation: 'sagittal',
|
|
1428
|
+
toolGroupId: toolGroupIds.fusion,
|
|
1429
|
+
initialImageOptions: {
|
|
1430
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1431
|
+
},
|
|
1432
|
+
syncGroups: [{
|
|
1433
|
+
type: 'cameraPosition',
|
|
1434
|
+
id: 'sagittalSync',
|
|
1435
|
+
source: true,
|
|
1436
|
+
target: true
|
|
1437
|
+
}, {
|
|
1438
|
+
type: 'voi',
|
|
1439
|
+
id: 'ctWLSync',
|
|
1440
|
+
source: false,
|
|
1441
|
+
target: true
|
|
1442
|
+
}, {
|
|
1443
|
+
type: 'voi',
|
|
1444
|
+
id: 'fusionWLSync',
|
|
1445
|
+
source: true,
|
|
1446
|
+
target: true
|
|
1447
|
+
}, {
|
|
1448
|
+
type: 'voi',
|
|
1449
|
+
id: 'ptFusionWLSync',
|
|
1450
|
+
source: false,
|
|
1451
|
+
target: true,
|
|
1452
|
+
options: {
|
|
1453
|
+
syncInvertState: false
|
|
1454
|
+
}
|
|
1455
|
+
}]
|
|
1456
|
+
},
|
|
1457
|
+
displaySets: [{
|
|
1458
|
+
id: 'ctDisplaySet'
|
|
1459
|
+
}, {
|
|
1460
|
+
options: {
|
|
1461
|
+
...getPTOptions(ptOptionsParams)
|
|
1462
|
+
},
|
|
1463
|
+
id: 'ptDisplaySet'
|
|
1464
|
+
}]
|
|
1465
|
+
}, {
|
|
1466
|
+
viewportOptions: {
|
|
1467
|
+
viewportId: 'fusionCoronal',
|
|
1468
|
+
viewportType: 'volume',
|
|
1469
|
+
orientation: 'coronal',
|
|
1470
|
+
toolGroupId: toolGroupIds.fusion,
|
|
1471
|
+
initialImageOptions: {
|
|
1472
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1473
|
+
},
|
|
1474
|
+
syncGroups: [{
|
|
1475
|
+
type: 'cameraPosition',
|
|
1476
|
+
id: 'coronalSync',
|
|
1477
|
+
source: true,
|
|
1478
|
+
target: true
|
|
1479
|
+
}, {
|
|
1480
|
+
type: 'voi',
|
|
1481
|
+
id: 'ctWLSync',
|
|
1482
|
+
source: false,
|
|
1483
|
+
target: true
|
|
1484
|
+
}, {
|
|
1485
|
+
type: 'voi',
|
|
1486
|
+
id: 'fusionWLSync',
|
|
1487
|
+
source: true,
|
|
1488
|
+
target: true
|
|
1489
|
+
}, {
|
|
1490
|
+
type: 'voi',
|
|
1491
|
+
id: 'ptFusionWLSync',
|
|
1492
|
+
source: false,
|
|
1493
|
+
target: true,
|
|
1494
|
+
options: {
|
|
1495
|
+
syncInvertState: false
|
|
1496
|
+
}
|
|
1497
|
+
}]
|
|
1498
|
+
},
|
|
1499
|
+
displaySets: [{
|
|
1500
|
+
id: 'ctDisplaySet'
|
|
1501
|
+
}, {
|
|
1502
|
+
options: {
|
|
1503
|
+
...getPTOptions(ptOptionsParams)
|
|
1504
|
+
},
|
|
1505
|
+
id: 'ptDisplaySet'
|
|
1506
|
+
}]
|
|
1507
|
+
}];
|
|
1508
|
+
}
|
|
1509
|
+
function getSeriesChartViewport() {
|
|
1510
|
+
return {
|
|
1511
|
+
viewportOptions: {
|
|
1512
|
+
viewportId: 'seriesChart'
|
|
1513
|
+
},
|
|
1514
|
+
displaySets: [{
|
|
1515
|
+
id: 'chartDisplaySet',
|
|
1516
|
+
options: {
|
|
1517
|
+
// This dataset does not require the download of any instance since it is pre-computed locally,
|
|
1518
|
+
// but interleaveTopToBottom.ts was not loading any series because it consider that all viewports
|
|
1519
|
+
// are a Cornerstone viewport which is not true in this case and it waits for all viewports to
|
|
1520
|
+
// have called interleaveTopToBottom(...).
|
|
1521
|
+
skipLoading: true
|
|
1522
|
+
}
|
|
1523
|
+
}]
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
function getCTViewports() {
|
|
1527
|
+
return [{
|
|
1528
|
+
viewportOptions: {
|
|
1529
|
+
viewportId: 'ctAxial',
|
|
1530
|
+
viewportType: 'volume',
|
|
1531
|
+
orientation: 'axial',
|
|
1532
|
+
toolGroupId: toolGroupIds.ct,
|
|
1533
|
+
initialImageOptions: {
|
|
1534
|
+
preset: 'middle' // 'first', 'last', 'middle'
|
|
1535
|
+
},
|
|
1536
|
+
syncGroups: [{
|
|
1537
|
+
type: 'cameraPosition',
|
|
1538
|
+
id: 'axialSync',
|
|
1539
|
+
source: true,
|
|
1540
|
+
target: true
|
|
1541
|
+
}, {
|
|
1542
|
+
type: 'voi',
|
|
1543
|
+
id: 'ctWLSync',
|
|
1544
|
+
source: true,
|
|
1545
|
+
target: true
|
|
1546
|
+
}]
|
|
1547
|
+
},
|
|
1548
|
+
displaySets: [{
|
|
1549
|
+
id: 'ctDisplaySet'
|
|
1550
|
+
}]
|
|
1551
|
+
}, {
|
|
1552
|
+
viewportOptions: {
|
|
1553
|
+
viewportId: 'ctSagittal',
|
|
1554
|
+
viewportType: 'volume',
|
|
1555
|
+
orientation: 'sagittal',
|
|
1556
|
+
toolGroupId: toolGroupIds.ct,
|
|
1557
|
+
initialImageOptions: {
|
|
1558
|
+
preset: 'middle'
|
|
1559
|
+
},
|
|
1560
|
+
syncGroups: [{
|
|
1561
|
+
type: 'cameraPosition',
|
|
1562
|
+
id: 'sagittalSync',
|
|
1563
|
+
source: true,
|
|
1564
|
+
target: true
|
|
1565
|
+
}, {
|
|
1566
|
+
type: 'voi',
|
|
1567
|
+
id: 'ctWLSync',
|
|
1568
|
+
source: true,
|
|
1569
|
+
target: true
|
|
1570
|
+
}]
|
|
1571
|
+
},
|
|
1572
|
+
displaySets: [{
|
|
1573
|
+
id: 'ctDisplaySet'
|
|
1574
|
+
}]
|
|
1575
|
+
}, {
|
|
1576
|
+
viewportOptions: {
|
|
1577
|
+
viewportId: 'ctCoronal',
|
|
1578
|
+
viewportType: 'volume',
|
|
1579
|
+
orientation: 'coronal',
|
|
1580
|
+
toolGroupId: toolGroupIds.ct,
|
|
1581
|
+
initialImageOptions: {
|
|
1582
|
+
preset: 'middle'
|
|
1583
|
+
},
|
|
1584
|
+
syncGroups: [{
|
|
1585
|
+
type: 'cameraPosition',
|
|
1586
|
+
id: 'coronalSync',
|
|
1587
|
+
source: true,
|
|
1588
|
+
target: true
|
|
1589
|
+
}, {
|
|
1590
|
+
type: 'voi',
|
|
1591
|
+
id: 'ctWLSync',
|
|
1592
|
+
source: true,
|
|
1593
|
+
target: true
|
|
1594
|
+
}]
|
|
1595
|
+
},
|
|
1596
|
+
displaySets: [{
|
|
1597
|
+
id: 'ctDisplaySet'
|
|
1598
|
+
}]
|
|
1599
|
+
}];
|
|
1600
|
+
}
|
|
1601
|
+
const defaultProtocol = {
|
|
1602
|
+
id: 'default4D',
|
|
1603
|
+
locked: true,
|
|
1604
|
+
// Don't store this hanging protocol as it applies to the currently active
|
|
1605
|
+
// display set by default
|
|
1606
|
+
// cacheId: null,
|
|
1607
|
+
hasUpdatedPriorsInformation: false,
|
|
1608
|
+
name: 'Default',
|
|
1609
|
+
createdDate: '2023-01-01T00:00:00.000Z',
|
|
1610
|
+
modifiedDate: '2023-01-01T00:00:00.000Z',
|
|
1611
|
+
availableTo: {},
|
|
1612
|
+
editableBy: {},
|
|
1613
|
+
imageLoadStrategy: 'default',
|
|
1614
|
+
// "default" , "interleaveTopToBottom", "interleaveCenter"
|
|
1615
|
+
protocolMatchingRules: [{
|
|
1616
|
+
attribute: 'ModalitiesInStudy',
|
|
1617
|
+
constraint: {
|
|
1618
|
+
contains: ['CT', 'PT']
|
|
1619
|
+
}
|
|
1620
|
+
}],
|
|
1621
|
+
// -1 would be used to indicate active only, whereas other values are
|
|
1622
|
+
// the number of required priors referenced - so 0 means active with
|
|
1623
|
+
// 0 or more priors.
|
|
1624
|
+
numberOfPriorsReferenced: -1,
|
|
1625
|
+
displaySetSelectors: {
|
|
1626
|
+
defaultDisplaySetId: {
|
|
1627
|
+
// Unused currently
|
|
1628
|
+
imageMatchingRules: [],
|
|
1629
|
+
// Matches displaysets, NOT series
|
|
1630
|
+
seriesMatchingRules: [
|
|
1631
|
+
// Try to match series with images by default, to prevent weird display
|
|
1632
|
+
// on SEG/SR containing studies
|
|
1633
|
+
{
|
|
1634
|
+
attribute: 'numImageFrames',
|
|
1635
|
+
constraint: {
|
|
1636
|
+
greaterThan: {
|
|
1637
|
+
value: 0
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}]
|
|
1641
|
+
// Can be used to select matching studies
|
|
1642
|
+
// studyMatchingRules: [],
|
|
1643
|
+
},
|
|
1644
|
+
ctDisplaySet: {
|
|
1645
|
+
// Unused currently
|
|
1646
|
+
imageMatchingRules: [],
|
|
1647
|
+
// Matches displaysets, NOT series
|
|
1648
|
+
seriesMatchingRules: [{
|
|
1649
|
+
attribute: 'Modality',
|
|
1650
|
+
constraint: {
|
|
1651
|
+
equals: {
|
|
1652
|
+
value: 'CT'
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
required: true
|
|
1656
|
+
}, {
|
|
1657
|
+
attribute: 'isReconstructable',
|
|
1658
|
+
constraint: {
|
|
1659
|
+
equals: {
|
|
1660
|
+
value: true
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
required: true
|
|
1664
|
+
}]
|
|
1665
|
+
// Can be used to select matching studies
|
|
1666
|
+
// studyMatchingRules: [],
|
|
1667
|
+
},
|
|
1668
|
+
ptDisplaySet: {
|
|
1669
|
+
// Unused currently
|
|
1670
|
+
imageMatchingRules: [],
|
|
1671
|
+
// Matches displaysets, NOT series
|
|
1672
|
+
seriesMatchingRules: [{
|
|
1673
|
+
attribute: 'Modality',
|
|
1674
|
+
constraint: {
|
|
1675
|
+
equals: 'PT'
|
|
1676
|
+
},
|
|
1677
|
+
required: true
|
|
1678
|
+
}, {
|
|
1679
|
+
attribute: 'isReconstructable',
|
|
1680
|
+
constraint: {
|
|
1681
|
+
equals: {
|
|
1682
|
+
value: true
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
required: true
|
|
1686
|
+
}, {
|
|
1687
|
+
attribute: 'SeriesDescription',
|
|
1688
|
+
constraint: {
|
|
1689
|
+
contains: 'Corrected'
|
|
1690
|
+
}
|
|
1691
|
+
}, {
|
|
1692
|
+
weight: 2,
|
|
1693
|
+
attribute: 'SeriesDescription',
|
|
1694
|
+
constraint: {
|
|
1695
|
+
doesNotContain: {
|
|
1696
|
+
value: 'Uncorrected'
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// Should we check if CorrectedImage contains ATTN?
|
|
1702
|
+
// (0028,0051) (CorrectedImage): NORM\DTIM\ATTN\SCAT\RADL\DECY
|
|
1703
|
+
]
|
|
1704
|
+
// Can be used to select matching studies
|
|
1705
|
+
// studyMatchingRules: [],
|
|
1706
|
+
},
|
|
1707
|
+
chartDisplaySet: {
|
|
1708
|
+
// Unused currently
|
|
1709
|
+
imageMatchingRules: [],
|
|
1710
|
+
// Matches displaysets, NOT series
|
|
1711
|
+
seriesMatchingRules: [{
|
|
1712
|
+
attribute: 'Modality',
|
|
1713
|
+
constraint: {
|
|
1714
|
+
equals: {
|
|
1715
|
+
value: 'CHT'
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
required: true
|
|
1719
|
+
}]
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
stages: [{
|
|
1723
|
+
id: 'dataPreparation',
|
|
1724
|
+
name: 'Data Preparation',
|
|
1725
|
+
viewportStructure: {
|
|
1726
|
+
layoutType: 'grid',
|
|
1727
|
+
properties: {
|
|
1728
|
+
rows: 1,
|
|
1729
|
+
columns: 3
|
|
1730
|
+
}
|
|
1731
|
+
},
|
|
1732
|
+
viewports: [...getPTViewports()],
|
|
1733
|
+
createdDate: '2023-01-01T00:00:00.000Z'
|
|
1734
|
+
}, {
|
|
1735
|
+
id: 'registration',
|
|
1736
|
+
name: 'Registration',
|
|
1737
|
+
viewportStructure: {
|
|
1738
|
+
layoutType: 'grid',
|
|
1739
|
+
properties: {
|
|
1740
|
+
rows: 3,
|
|
1741
|
+
columns: 3
|
|
1742
|
+
}
|
|
1743
|
+
},
|
|
1744
|
+
viewports: [...getFusionViewports(), ...getCTViewports(), ...getPTViewports()],
|
|
1745
|
+
createdDate: '2023-01-01T00:00:00.000Z'
|
|
1746
|
+
}, {
|
|
1747
|
+
id: 'roiQuantification',
|
|
1748
|
+
name: 'ROI Quantification',
|
|
1749
|
+
viewportStructure: {
|
|
1750
|
+
layoutType: 'grid',
|
|
1751
|
+
properties: {
|
|
1752
|
+
rows: 1,
|
|
1753
|
+
columns: 3
|
|
1754
|
+
}
|
|
1755
|
+
},
|
|
1756
|
+
viewports: [...getFusionViewports()],
|
|
1757
|
+
createdDate: '2023-01-01T00:00:00.000Z'
|
|
1758
|
+
}, {
|
|
1759
|
+
id: 'kineticAnalysis',
|
|
1760
|
+
name: 'Kinetic Analysis',
|
|
1761
|
+
viewportStructure: {
|
|
1762
|
+
layoutType: 'grid',
|
|
1763
|
+
properties: {
|
|
1764
|
+
rows: 2,
|
|
1765
|
+
columns: 3,
|
|
1766
|
+
layoutOptions: [{
|
|
1767
|
+
x: 0,
|
|
1768
|
+
y: 0,
|
|
1769
|
+
width: 1 / 3,
|
|
1770
|
+
height: 1 / 2
|
|
1771
|
+
}, {
|
|
1772
|
+
x: 1 / 3,
|
|
1773
|
+
y: 0,
|
|
1774
|
+
width: 1 / 3,
|
|
1775
|
+
height: 1 / 2
|
|
1776
|
+
}, {
|
|
1777
|
+
x: 2 / 3,
|
|
1778
|
+
y: 0,
|
|
1779
|
+
width: 1 / 3,
|
|
1780
|
+
height: 1 / 2
|
|
1781
|
+
}, {
|
|
1782
|
+
x: 0,
|
|
1783
|
+
y: 1 / 2,
|
|
1784
|
+
width: 1,
|
|
1785
|
+
height: 1 / 2
|
|
1786
|
+
}]
|
|
1787
|
+
}
|
|
1788
|
+
},
|
|
1789
|
+
viewports: [...getFusionViewports(), getSeriesChartViewport()],
|
|
1790
|
+
createdDate: '2023-01-01T00:00:00.000Z'
|
|
1791
|
+
}]
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* HangingProtocolModule should provide a list of hanging protocols that will be
|
|
1796
|
+
* available in OHIF for Modes to use to decide on the structure of the viewports
|
|
1797
|
+
* and also the series that hung in the viewports. Each hanging protocol is defined by
|
|
1798
|
+
* { name, protocols}. Examples include the default hanging protocol provided by
|
|
1799
|
+
* the default extension that shows 2x2 viewports.
|
|
1800
|
+
*/
|
|
1801
|
+
|
|
1802
|
+
function getHangingProtocolModule() {
|
|
1803
|
+
return [{
|
|
1804
|
+
name: defaultProtocol.id,
|
|
1805
|
+
protocol: defaultProtocol
|
|
1806
|
+
}];
|
|
1807
|
+
}
|
|
1808
|
+
/* harmony default export */ const src_getHangingProtocolModule = (getHangingProtocolModule);
|
|
1809
|
+
;// CONCATENATED MODULE: ../../../extensions/cornerstone-dynamic-volume/src/index.ts
|
|
1810
|
+
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* You can remove any of the following modules if you don't need them.
|
|
1818
|
+
*/
|
|
1819
|
+
const dynamicVolumeExtension = {
|
|
1820
|
+
/**
|
|
1821
|
+
* Only required property. Should be a unique value across all extensions.
|
|
1822
|
+
* You ID can be anything you want, but it should be unique.
|
|
1823
|
+
*/
|
|
1824
|
+
id: id,
|
|
1825
|
+
/**
|
|
1826
|
+
* Perform any pre-registration tasks here. This is called before the extension
|
|
1827
|
+
* is registered. Usually we run tasks such as: configuring the libraries
|
|
1828
|
+
* (e.g. cornerstone, cornerstoneTools, ...) or registering any services that
|
|
1829
|
+
* this extension is providing.
|
|
1830
|
+
*/
|
|
1831
|
+
preRegistration: ({
|
|
1832
|
+
servicesManager,
|
|
1833
|
+
commandsManager,
|
|
1834
|
+
configuration = {}
|
|
1835
|
+
}) => {
|
|
1836
|
+
// TODO: look for the right fix
|
|
1837
|
+
esm.cache.setMaxCacheSize(5 * 1024 * 1024 * 1024);
|
|
1838
|
+
},
|
|
1839
|
+
/**
|
|
1840
|
+
* PanelModule should provide a list of panels that will be available in OHIF
|
|
1841
|
+
* for Modes to consume and render. Each panel is defined by a {name,
|
|
1842
|
+
* iconName, iconLabel, label, component} object. Example of a panel module
|
|
1843
|
+
* is the StudyBrowserPanel that is provided by the default extension in OHIF.
|
|
1844
|
+
*/
|
|
1845
|
+
getPanelModule: src_getPanelModule,
|
|
1846
|
+
/**
|
|
1847
|
+
* ViewportModule should provide a list of viewports that will be available in OHIF
|
|
1848
|
+
* for Modes to consume and use in the viewports. Each viewport is defined by
|
|
1849
|
+
* {name, component} object. Example of a viewport module is the CornerstoneViewport
|
|
1850
|
+
* that is provided by the Cornerstone extension in OHIF.
|
|
1851
|
+
*/
|
|
1852
|
+
getHangingProtocolModule: src_getHangingProtocolModule,
|
|
1853
|
+
/**
|
|
1854
|
+
* CommandsModule should provide a list of commands that will be available in OHIF
|
|
1855
|
+
* for Modes to consume and use in the viewports. Each command is defined by
|
|
1856
|
+
* an object of { actions, definitions, defaultContext } where actions is an
|
|
1857
|
+
* object of functions, definitions is an object of available commands, their
|
|
1858
|
+
* options, and defaultContext is the default context for the command to run against.
|
|
1859
|
+
*/
|
|
1860
|
+
getCommandsModule: ({
|
|
1861
|
+
servicesManager,
|
|
1862
|
+
commandsManager,
|
|
1863
|
+
extensionManager
|
|
1864
|
+
}) => {
|
|
1865
|
+
return src_commandsModule({
|
|
1866
|
+
servicesManager,
|
|
1867
|
+
commandsManager,
|
|
1868
|
+
extensionManager
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
/***/ })
|
|
1875
|
+
|
|
1876
|
+
}]);
|