larvitar 1.5.14 → 2.0.2
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/.vscode/settings.json +4 -0
- package/README.md +78 -48
- package/bundler/webpack.common.js +27 -0
- package/bundler/webpack.dev.js +23 -0
- package/bundler/webpack.prod.js +19 -0
- package/decs.d.ts +12 -0
- package/dist/imaging/MetaDataReadable.d.ts +40 -0
- package/dist/imaging/MetaDataTypes.d.ts +3489 -0
- package/dist/imaging/imageAnonymization.d.ts +12 -0
- package/dist/imaging/imageColormaps.d.ts +47 -0
- package/dist/imaging/imageContours.d.ts +18 -0
- package/dist/imaging/imageIo.d.ts +42 -0
- package/dist/imaging/imageLayers.d.ts +56 -0
- package/dist/imaging/imageLoading.d.ts +65 -0
- package/dist/imaging/imageParsing.d.ts +46 -0
- package/dist/imaging/imagePresets.d.ts +43 -0
- package/dist/imaging/imageRendering.d.ts +238 -0
- package/dist/imaging/imageReslice.d.ts +14 -0
- package/dist/imaging/imageStore.d.ts +121 -0
- package/dist/imaging/imageTags.d.ts +22 -0
- package/dist/imaging/imageTools.d.ts +20 -0
- package/dist/imaging/imageUtils.d.ts +165 -0
- package/dist/imaging/loaders/commonLoader.d.ts +103 -0
- package/dist/imaging/loaders/dicomLoader.d.ts +29 -0
- package/dist/imaging/loaders/fileLoader.d.ts +33 -0
- package/dist/imaging/loaders/multiframeLoader.d.ts +37 -0
- package/dist/imaging/loaders/nrrdLoader.d.ts +113 -0
- package/dist/imaging/loaders/resliceLoader.d.ts +15 -0
- package/dist/imaging/monitors/memory.d.ts +41 -0
- package/dist/imaging/monitors/performance.d.ts +23 -0
- package/dist/imaging/parsers/ecg.d.ts +15 -0
- package/dist/imaging/parsers/nrrd.d.ts +3 -0
- package/dist/imaging/tools/custom/4dSliceScrollTool.d.ts +12 -0
- package/dist/imaging/tools/custom/BorderMagnifyTool.d.ts +18 -0
- package/dist/imaging/tools/custom/contourTool.d.ts +409 -0
- package/dist/imaging/tools/custom/diameterTool.d.ts +18 -0
- package/dist/imaging/tools/custom/editMaskTool.d.ts +22 -0
- package/dist/imaging/tools/custom/ellipticalRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/polygonSegmentationMixin.d.ts +54 -0
- package/dist/imaging/tools/custom/polylineScissorsTool.d.ts +11 -0
- package/dist/imaging/tools/custom/rectangleRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/seedTool.d.ts +0 -0
- package/dist/imaging/tools/custom/setLabelMap3D.d.ts +39 -0
- package/dist/imaging/tools/custom/thresholdsBrushTool.d.ts +19 -0
- package/dist/imaging/tools/default.d.ts +53 -0
- package/dist/imaging/tools/interaction.d.ts +30 -0
- package/dist/imaging/tools/io.d.ts +38 -0
- package/dist/imaging/tools/main.d.ts +81 -0
- package/dist/imaging/tools/segmentation.d.ts +125 -0
- package/dist/imaging/tools/state.d.ts +17 -0
- package/dist/imaging/tools/strategies/eraseFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/fillFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/index.d.ts +2 -0
- package/dist/imaging/waveforms/ecg.d.ts +39 -0
- package/dist/index.d.ts +35 -0
- package/dist/larvitar.js +90104 -0
- package/dist/larvitar.js.map +1 -0
- package/imaging/MetaDataReadable.ts +41 -0
- package/imaging/MetaDataTypes.ts +3491 -0
- package/imaging/dataDictionary.json +5328 -5328
- package/imaging/{imageAnonymization.js → imageAnonymization.ts} +41 -13
- package/imaging/{imageColormaps.js → imageColormaps.ts} +48 -30
- package/imaging/{imageContours.js → imageContours.ts} +24 -22
- package/imaging/{imageIo.js → imageIo.ts} +89 -52
- package/imaging/{imageLayers.js → imageLayers.ts} +31 -14
- package/imaging/{imageLoading.js → imageLoading.ts} +107 -43
- package/imaging/{imageParsing.js → imageParsing.ts} +160 -80
- package/imaging/{imagePresets.js → imagePresets.ts} +44 -11
- package/imaging/imageRendering.ts +1091 -0
- package/imaging/{imageReslice.js → imageReslice.ts} +18 -9
- package/imaging/imageStore.ts +487 -0
- package/imaging/imageTags.ts +609 -0
- package/imaging/imageTools.js +2 -1
- package/imaging/{imageUtils.js → imageUtils.ts} +211 -701
- package/imaging/loaders/{commonLoader.js → commonLoader.ts} +73 -24
- package/imaging/loaders/{dicomLoader.js → dicomLoader.ts} +25 -5
- package/imaging/loaders/{fileLoader.js → fileLoader.ts} +5 -5
- package/imaging/loaders/{multiframeLoader.js → multiframeLoader.ts} +145 -90
- package/imaging/loaders/{nrrdLoader.js → nrrdLoader.ts} +231 -64
- package/imaging/loaders/{resliceLoader.js → resliceLoader.ts} +51 -20
- package/imaging/monitors/{memory.js → memory.ts} +54 -8
- package/imaging/monitors/performance.ts +34 -0
- package/imaging/parsers/ecg.ts +54 -0
- package/imaging/tools/README.md +27 -0
- package/imaging/tools/custom/4dSliceScrollTool.js +47 -46
- package/imaging/tools/custom/BorderMagnifyTool.js +99 -0
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +534 -0
- package/imaging/tools/custom/polylineScissorsTool.js +1 -1
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +564 -0
- package/imaging/tools/{setLabelMap3D.js → custom/setLabelMap3D.ts} +19 -25
- package/imaging/tools/{default.js → default.ts} +119 -33
- package/imaging/tools/{interaction.js → interaction.ts} +42 -23
- package/imaging/tools/{io.js → io.ts} +47 -31
- package/imaging/tools/{main.js → main.ts} +105 -40
- package/imaging/tools/{segmentation.js → segmentation.ts} +95 -68
- package/imaging/tools/{state.js → state.ts} +7 -12
- package/imaging/tools/types.d.ts +243 -0
- package/imaging/types.d.ts +200 -0
- package/imaging/waveforms/ecg.ts +191 -0
- package/{index.js → index.ts} +53 -14
- package/jsdoc.json +1 -1
- package/package.json +35 -14
- package/tsconfig.json +102 -0
- package/imaging/imageRendering.js +0 -860
- package/imaging/imageStore.js +0 -322
- package/modules/vuex/larvitar.js +0 -187
- /package/imaging/tools/{polygonSegmentationMixin.js → custom/polygonSegmentationMixin.js} +0 -0
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
getLarvitarImageTracker,
|
|
14
14
|
getLarvitarManager
|
|
15
15
|
} from "./loaders/commonLoader";
|
|
16
|
-
import
|
|
16
|
+
import store from "./imageStore";
|
|
17
|
+
import { Series } from "./types";
|
|
17
18
|
|
|
18
19
|
/*
|
|
19
20
|
* This module provides the following functions to be exported:
|
|
@@ -26,12 +27,14 @@ import { larvitar_store } from "./imageStore";
|
|
|
26
27
|
* @function resliceSeries
|
|
27
28
|
* @param {Object} seriesData the original series data
|
|
28
29
|
* @param {String} orientation the reslice orientation [coronal or sagittal]
|
|
29
|
-
* @param {String} seriesId the series id
|
|
30
30
|
* @returns {Promise} - Return a promise which will resolve when data is available
|
|
31
31
|
*/
|
|
32
|
-
export function resliceSeries(
|
|
32
|
+
export function resliceSeries(
|
|
33
|
+
seriesData: Series,
|
|
34
|
+
orientation: "axial" | "coronal" | "sagittal"
|
|
35
|
+
) {
|
|
33
36
|
let reslicePromise = new Promise(resolve => {
|
|
34
|
-
let reslicedSeries = {};
|
|
37
|
+
let reslicedSeries: Partial<Series> = {};
|
|
35
38
|
let reslicedSeriesId = uuidv4();
|
|
36
39
|
let reslicedMetaData = getReslicedMetadata(
|
|
37
40
|
reslicedSeriesId,
|
|
@@ -42,37 +45,43 @@ export function resliceSeries(seriesData, orientation) {
|
|
|
42
45
|
);
|
|
43
46
|
|
|
44
47
|
reslicedSeries.imageIds = reslicedMetaData.imageIds;
|
|
48
|
+
// @ts-ignore fix incompatibilities between these types
|
|
45
49
|
reslicedSeries.instances = reslicedMetaData.instances;
|
|
46
50
|
|
|
47
51
|
reslicedSeries.currentImageIdIndex = Math.floor(
|
|
48
52
|
reslicedSeries.imageIds.length / 2
|
|
49
53
|
);
|
|
50
54
|
|
|
51
|
-
function computeReslice(
|
|
55
|
+
function computeReslice(
|
|
56
|
+
seriesData: Series,
|
|
57
|
+
reslicedSeriesId: string,
|
|
58
|
+
reslicedSeries: Series
|
|
59
|
+
) {
|
|
52
60
|
let t0 = performance.now();
|
|
53
61
|
let imageTracker = getLarvitarImageTracker();
|
|
54
62
|
let manager = getLarvitarManager();
|
|
55
|
-
each(reslicedSeries.imageIds, function (imageId) {
|
|
63
|
+
each(reslicedSeries.imageIds, function (imageId: string) {
|
|
56
64
|
reslicedSeries.instances[imageId].pixelData = getReslicedPixeldata(
|
|
57
65
|
imageId,
|
|
58
66
|
seriesData,
|
|
59
67
|
reslicedSeries
|
|
60
|
-
);
|
|
68
|
+
) as Uint16Array;
|
|
61
69
|
imageTracker[imageId] = reslicedSeriesId;
|
|
62
70
|
});
|
|
63
|
-
|
|
71
|
+
store.addSeriesId(reslicedSeriesId, reslicedSeries.imageIds);
|
|
64
72
|
reslicedSeries.numberOfImages = reslicedSeries.imageIds.length;
|
|
65
73
|
reslicedSeries.seriesUID = reslicedSeriesId;
|
|
66
74
|
reslicedSeries.seriesDescription = seriesData.seriesDescription;
|
|
67
75
|
reslicedSeries.orientation = orientation;
|
|
68
76
|
manager[reslicedSeriesId] = reslicedSeries;
|
|
77
|
+
//@ts-ignore deprecated
|
|
69
78
|
manager[seriesData.seriesUID][orientation] = reslicedSeriesId;
|
|
70
79
|
let t1 = performance.now();
|
|
71
80
|
console.log(`Call to resliceSeries took ${t1 - t0} milliseconds.`);
|
|
72
81
|
resolve(reslicedSeries);
|
|
73
82
|
}
|
|
74
83
|
// reslice the data
|
|
75
|
-
computeReslice(seriesData, reslicedSeriesId, reslicedSeries);
|
|
84
|
+
computeReslice(seriesData, reslicedSeriesId, reslicedSeries as Series);
|
|
76
85
|
});
|
|
77
86
|
return reslicePromise;
|
|
78
87
|
}
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/** @module imaging/imageStore
|
|
2
|
+
* @desc This file provides functionalities
|
|
3
|
+
* for data config store.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// external libraries
|
|
7
|
+
import { get as _get, cloneDeep as _cloneDeep } from "lodash";
|
|
8
|
+
|
|
9
|
+
type StoreSeries = { imageIds: string[]; progress: number };
|
|
10
|
+
|
|
11
|
+
type Store = {
|
|
12
|
+
colormapId: string;
|
|
13
|
+
errorLog: string; // TODO review this, should be an array?
|
|
14
|
+
leftActiveTool?: string;
|
|
15
|
+
rightActiveTool?: string;
|
|
16
|
+
series: { [seriesUID: string]: StoreSeries };
|
|
17
|
+
viewports: { [key: string]: typeof DEFAULT_VIEWPORT };
|
|
18
|
+
// fallback for any other field
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type SetPayload =
|
|
23
|
+
| ["errorLog" | "leftActiveTool" | "rightActiveTool", string]
|
|
24
|
+
| [
|
|
25
|
+
"isColor" | "isMultiframe" | "isPDF" | "isTimeserie" | "ready",
|
|
26
|
+
string,
|
|
27
|
+
boolean
|
|
28
|
+
]
|
|
29
|
+
| [
|
|
30
|
+
(
|
|
31
|
+
| "progress"
|
|
32
|
+
| "loading"
|
|
33
|
+
| "minPixelValue"
|
|
34
|
+
| "maxPixelValue"
|
|
35
|
+
| "minSliceId"
|
|
36
|
+
| "maxSliceId"
|
|
37
|
+
| "minTimeId"
|
|
38
|
+
| "maxTimeId"
|
|
39
|
+
| "rotation"
|
|
40
|
+
| "scale"
|
|
41
|
+
| "sliceId"
|
|
42
|
+
| "timeId"
|
|
43
|
+
| "thickness"
|
|
44
|
+
),
|
|
45
|
+
string,
|
|
46
|
+
number
|
|
47
|
+
]
|
|
48
|
+
| ["timestamp", string, number | undefined]
|
|
49
|
+
| ["pendingSliceId", string, number | undefined]
|
|
50
|
+
| ["timestamps" | "timeIds", string, number[]]
|
|
51
|
+
| [
|
|
52
|
+
"contrast" | "dimensions" | "spacing" | "translation",
|
|
53
|
+
string,
|
|
54
|
+
number,
|
|
55
|
+
number
|
|
56
|
+
]
|
|
57
|
+
| [
|
|
58
|
+
"defaultViewport",
|
|
59
|
+
string,
|
|
60
|
+
number,
|
|
61
|
+
number,
|
|
62
|
+
number,
|
|
63
|
+
number,
|
|
64
|
+
number,
|
|
65
|
+
number,
|
|
66
|
+
boolean
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Larvitar store object
|
|
70
|
+
let STORE: Store;
|
|
71
|
+
|
|
72
|
+
// Data listeners
|
|
73
|
+
let storeListener: ((data: Store) => {}) | undefined = undefined;
|
|
74
|
+
const seriesListeners = {} as {
|
|
75
|
+
[seriesId: string]: (data: StoreSeries) => {};
|
|
76
|
+
};
|
|
77
|
+
const viewportsListeners = {} as {
|
|
78
|
+
[elementId: string]: (data: typeof DEFAULT_VIEWPORT) => {};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// default initial store object
|
|
82
|
+
const INITIAL_STORE_DATA: Store = {
|
|
83
|
+
colormapId: "gray",
|
|
84
|
+
errorLog: "",
|
|
85
|
+
leftActiveTool: undefined,
|
|
86
|
+
rightActiveTool: undefined,
|
|
87
|
+
series: {},
|
|
88
|
+
viewports: {}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// default viewport object
|
|
92
|
+
export const DEFAULT_VIEWPORT: {
|
|
93
|
+
loading: number;
|
|
94
|
+
ready: boolean;
|
|
95
|
+
minSliceId: number;
|
|
96
|
+
maxSliceId: number;
|
|
97
|
+
sliceId: number;
|
|
98
|
+
pendingSliceId?: number;
|
|
99
|
+
minTimeId: number;
|
|
100
|
+
maxTimeId: number;
|
|
101
|
+
timeId: number;
|
|
102
|
+
timestamp: number;
|
|
103
|
+
timestamps: number[];
|
|
104
|
+
timeIds: number[];
|
|
105
|
+
rows: number;
|
|
106
|
+
cols: number;
|
|
107
|
+
spacing_x: number;
|
|
108
|
+
spacing_y: number;
|
|
109
|
+
thickness: number;
|
|
110
|
+
minPixelValue: number;
|
|
111
|
+
maxPixelValue: number;
|
|
112
|
+
isColor: boolean;
|
|
113
|
+
isMultiframe: boolean;
|
|
114
|
+
isTimeserie: boolean;
|
|
115
|
+
isPDF: boolean;
|
|
116
|
+
viewport: {
|
|
117
|
+
scale: number;
|
|
118
|
+
rotation: number;
|
|
119
|
+
translation: {
|
|
120
|
+
x: number;
|
|
121
|
+
y: number;
|
|
122
|
+
};
|
|
123
|
+
voi: {
|
|
124
|
+
windowCenter: number;
|
|
125
|
+
windowWidth: number;
|
|
126
|
+
};
|
|
127
|
+
// redundant fields ?
|
|
128
|
+
rows: number;
|
|
129
|
+
cols: number;
|
|
130
|
+
spacing_x: number;
|
|
131
|
+
spacing_y: number;
|
|
132
|
+
thickness: number;
|
|
133
|
+
};
|
|
134
|
+
default: {
|
|
135
|
+
scale: number;
|
|
136
|
+
rotation: number;
|
|
137
|
+
translation: {
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
};
|
|
141
|
+
voi: {
|
|
142
|
+
windowCenter: number;
|
|
143
|
+
windowWidth: number;
|
|
144
|
+
invert: boolean;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
} = {
|
|
148
|
+
loading: 0, // from 0 to 100 (%)
|
|
149
|
+
ready: false, // true when currentImageId is rendered
|
|
150
|
+
minSliceId: 0,
|
|
151
|
+
maxSliceId: 0,
|
|
152
|
+
sliceId: 0,
|
|
153
|
+
pendingSliceId: undefined,
|
|
154
|
+
minTimeId: 0,
|
|
155
|
+
maxTimeId: 0,
|
|
156
|
+
timeId: 0,
|
|
157
|
+
timestamp: 0,
|
|
158
|
+
timestamps: [],
|
|
159
|
+
timeIds: [],
|
|
160
|
+
rows: 0,
|
|
161
|
+
cols: 0,
|
|
162
|
+
spacing_x: 0.0,
|
|
163
|
+
spacing_y: 0.0,
|
|
164
|
+
thickness: 0.0,
|
|
165
|
+
minPixelValue: 0,
|
|
166
|
+
maxPixelValue: 0,
|
|
167
|
+
isColor: false,
|
|
168
|
+
isMultiframe: false,
|
|
169
|
+
isTimeserie: false,
|
|
170
|
+
isPDF: false,
|
|
171
|
+
viewport: {
|
|
172
|
+
scale: 0.0,
|
|
173
|
+
rotation: 0.0,
|
|
174
|
+
translation: {
|
|
175
|
+
x: 0.0,
|
|
176
|
+
y: 0.0
|
|
177
|
+
},
|
|
178
|
+
voi: {
|
|
179
|
+
windowCenter: 0.0,
|
|
180
|
+
windowWidth: 0.0
|
|
181
|
+
},
|
|
182
|
+
// redundant fields ?
|
|
183
|
+
rows: 0,
|
|
184
|
+
cols: 0,
|
|
185
|
+
spacing_x: 0.0,
|
|
186
|
+
spacing_y: 0.0,
|
|
187
|
+
thickness: 0.0
|
|
188
|
+
},
|
|
189
|
+
default: {
|
|
190
|
+
scale: 0.0,
|
|
191
|
+
rotation: 0.0,
|
|
192
|
+
translation: {
|
|
193
|
+
x: 0.0,
|
|
194
|
+
y: 0.0
|
|
195
|
+
},
|
|
196
|
+
voi: {
|
|
197
|
+
windowCenter: 0.0,
|
|
198
|
+
windowWidth: 0.0,
|
|
199
|
+
invert: false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export type Viewport = typeof DEFAULT_VIEWPORT;
|
|
205
|
+
|
|
206
|
+
// Trigger store listeners
|
|
207
|
+
const triggerStoreListener = (data: Store) =>
|
|
208
|
+
storeListener ? storeListener(data) : undefined;
|
|
209
|
+
|
|
210
|
+
const triggerViewportListener = (elementId: string) => {
|
|
211
|
+
if (viewportsListeners[elementId] && STORE?.viewports[elementId]) {
|
|
212
|
+
viewportsListeners[elementId](STORE.viewports[elementId]);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const triggerSeriesListener = (seriesId: string) => {
|
|
217
|
+
if (seriesListeners[seriesId] && STORE?.series[seriesId]) {
|
|
218
|
+
seriesListeners[seriesId](STORE.series[seriesId]);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Set a value into store
|
|
224
|
+
* @function setValue
|
|
225
|
+
* @param {Object} data - The data object
|
|
226
|
+
*/
|
|
227
|
+
const setValue = (store: Store, data: SetPayload) => {
|
|
228
|
+
let field = data[0];
|
|
229
|
+
const k = data[1];
|
|
230
|
+
let [_1, _2, ...v] = data;
|
|
231
|
+
|
|
232
|
+
const viewport = store.viewports[k];
|
|
233
|
+
|
|
234
|
+
// assign values
|
|
235
|
+
switch (field) {
|
|
236
|
+
case "progress":
|
|
237
|
+
if (!store.series[k]) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
store.series[k][field] = (v as [number])[0];
|
|
241
|
+
triggerSeriesListener(k);
|
|
242
|
+
break;
|
|
243
|
+
|
|
244
|
+
case "isColor":
|
|
245
|
+
case "isMultiframe":
|
|
246
|
+
case "isPDF":
|
|
247
|
+
case "isTimeserie":
|
|
248
|
+
case "ready":
|
|
249
|
+
if (!viewport) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
viewport[field] = (v as [boolean])[0];
|
|
253
|
+
triggerViewportListener(k);
|
|
254
|
+
break;
|
|
255
|
+
|
|
256
|
+
case "loading":
|
|
257
|
+
case "minPixelValue":
|
|
258
|
+
case "maxPixelValue":
|
|
259
|
+
case "minSliceId":
|
|
260
|
+
case "maxSliceId":
|
|
261
|
+
case "minTimeId":
|
|
262
|
+
case "maxTimeId":
|
|
263
|
+
case "sliceId":
|
|
264
|
+
case "pendingSliceId":
|
|
265
|
+
case "timeId":
|
|
266
|
+
case "timestamp":
|
|
267
|
+
if (!viewport) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
viewport[field] = (v as [number])[0];
|
|
271
|
+
triggerViewportListener(k);
|
|
272
|
+
break;
|
|
273
|
+
|
|
274
|
+
case "timestamps":
|
|
275
|
+
case "timeIds":
|
|
276
|
+
if (!viewport) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
viewport[field] = (v as [[number]])[0];
|
|
280
|
+
triggerViewportListener(k);
|
|
281
|
+
break;
|
|
282
|
+
|
|
283
|
+
case "rotation":
|
|
284
|
+
case "scale":
|
|
285
|
+
if (!viewport) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
viewport.viewport[field] = (v as [number])[0];
|
|
289
|
+
triggerViewportListener(k);
|
|
290
|
+
break;
|
|
291
|
+
|
|
292
|
+
case "thickness":
|
|
293
|
+
if (!viewport) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
viewport[field] = (v as [number])[0];
|
|
297
|
+
viewport.viewport[field] = (v as [number])[0];
|
|
298
|
+
triggerViewportListener(k);
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case "translation":
|
|
302
|
+
if (!viewport) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
v = v as [number, number];
|
|
306
|
+
viewport.viewport[field] = { x: v[0], y: v[1] };
|
|
307
|
+
triggerViewportListener(k);
|
|
308
|
+
break;
|
|
309
|
+
|
|
310
|
+
case "contrast":
|
|
311
|
+
if (!viewport) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
v = v as [number, number];
|
|
315
|
+
viewport.viewport.voi.windowWidth = v[0];
|
|
316
|
+
viewport.viewport.voi.windowCenter = v[1];
|
|
317
|
+
triggerViewportListener(k);
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case "dimensions":
|
|
321
|
+
if (!viewport) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
v = v as [number, number];
|
|
325
|
+
viewport.rows = v[0];
|
|
326
|
+
viewport.cols = v[1];
|
|
327
|
+
viewport.viewport.rows = v[0];
|
|
328
|
+
viewport.viewport.cols = v[1];
|
|
329
|
+
triggerViewportListener(k);
|
|
330
|
+
break;
|
|
331
|
+
|
|
332
|
+
case "spacing":
|
|
333
|
+
if (!viewport) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
v = v as [number, number];
|
|
337
|
+
viewport.spacing_x = v[0];
|
|
338
|
+
viewport.spacing_y = v[1];
|
|
339
|
+
viewport.viewport.spacing_x = v[0];
|
|
340
|
+
viewport.viewport.spacing_y = v[1];
|
|
341
|
+
triggerViewportListener(k);
|
|
342
|
+
break;
|
|
343
|
+
|
|
344
|
+
case "defaultViewport":
|
|
345
|
+
if (!viewport) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
v = v as [number, number, number, number, number, number, boolean];
|
|
349
|
+
viewport.default.scale = v[0];
|
|
350
|
+
viewport.default.rotation = v[1];
|
|
351
|
+
viewport.default.translation.x = v[2];
|
|
352
|
+
viewport.default.translation.y = v[3];
|
|
353
|
+
viewport.default.voi.windowWidth = v[4];
|
|
354
|
+
viewport.default.voi.windowCenter = v[5];
|
|
355
|
+
viewport.default.voi.invert = v[6];
|
|
356
|
+
triggerViewportListener(k);
|
|
357
|
+
break;
|
|
358
|
+
|
|
359
|
+
default:
|
|
360
|
+
store[field] = k;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Instancing the store
|
|
367
|
+
*/
|
|
368
|
+
const setup = (data = _cloneDeep(INITIAL_STORE_DATA)) => {
|
|
369
|
+
/**
|
|
370
|
+
* Create the Proxy handler object
|
|
371
|
+
* @param {String} name The namespace
|
|
372
|
+
* @param {Object} data The data object
|
|
373
|
+
* @return {Object} The Proxy handler
|
|
374
|
+
*/
|
|
375
|
+
const handler: ProxyHandler<Store> = {
|
|
376
|
+
get: (obj, prop: string) => {
|
|
377
|
+
if (prop === "_isProxy") return true;
|
|
378
|
+
if (
|
|
379
|
+
["object", "array"].includes(
|
|
380
|
+
Object.prototype.toString.call(obj[prop]).slice(8, -1).toLowerCase()
|
|
381
|
+
) &&
|
|
382
|
+
!obj[prop]._isProxy
|
|
383
|
+
) {
|
|
384
|
+
obj[prop] = new Proxy<Store>(obj[prop], handler);
|
|
385
|
+
}
|
|
386
|
+
return obj[prop];
|
|
387
|
+
},
|
|
388
|
+
set: (obj, prop: string, value) => {
|
|
389
|
+
// console.warn("SET", obj, prop, value);
|
|
390
|
+
if (obj[prop] === value) return true;
|
|
391
|
+
obj[prop] = value;
|
|
392
|
+
triggerStoreListener(data);
|
|
393
|
+
return true;
|
|
394
|
+
},
|
|
395
|
+
deleteProperty: (obj, prop: string) => {
|
|
396
|
+
delete obj[prop];
|
|
397
|
+
triggerStoreListener(data);
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
return new Proxy<Store>(data, handler);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const initializeStore = () => {
|
|
406
|
+
STORE = setup();
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const validateStore = () => {
|
|
410
|
+
if (!STORE) {
|
|
411
|
+
throw "Larvitar store does not exists. Initialize it with the 'initializeStore' function.";
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
export const set = (payload: SetPayload) => {
|
|
416
|
+
validateStore();
|
|
417
|
+
setValue(STORE!, payload);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
export default {
|
|
421
|
+
initialize: initializeStore,
|
|
422
|
+
// add/remove viewports
|
|
423
|
+
addViewport: (name: string) => {
|
|
424
|
+
validateStore();
|
|
425
|
+
STORE!.viewports[name] = _cloneDeep(DEFAULT_VIEWPORT);
|
|
426
|
+
},
|
|
427
|
+
deleteViewport: (name: string) => {
|
|
428
|
+
validateStore();
|
|
429
|
+
delete STORE!.viewports[name];
|
|
430
|
+
},
|
|
431
|
+
// add/remove series instances ids
|
|
432
|
+
addSeriesId: (seriesId: string, imageIds: string[]) => {
|
|
433
|
+
validateStore();
|
|
434
|
+
if (!STORE!.series[seriesId]) {
|
|
435
|
+
STORE!.series[seriesId] = {} as StoreSeries;
|
|
436
|
+
}
|
|
437
|
+
STORE!.series[seriesId].imageIds = imageIds;
|
|
438
|
+
triggerSeriesListener(seriesId);
|
|
439
|
+
},
|
|
440
|
+
removeSeriesId: (seriesId: string) => {
|
|
441
|
+
validateStore();
|
|
442
|
+
delete STORE!.series[seriesId];
|
|
443
|
+
},
|
|
444
|
+
resetSeriesIds: () => {
|
|
445
|
+
validateStore();
|
|
446
|
+
STORE!.series = {};
|
|
447
|
+
},
|
|
448
|
+
// expose useful sets
|
|
449
|
+
setSliceId: (elementId: string, imageIndex: number) => {
|
|
450
|
+
set(["sliceId", elementId, imageIndex]);
|
|
451
|
+
},
|
|
452
|
+
setPendingSliceId: (elementId: string, imageIndex: number) => {
|
|
453
|
+
set(["pendingSliceId", elementId, imageIndex]);
|
|
454
|
+
},
|
|
455
|
+
setMaxSliceId: (elementId: string, imageIndex: number) => {
|
|
456
|
+
set(["maxSliceId", elementId, imageIndex]);
|
|
457
|
+
},
|
|
458
|
+
// get
|
|
459
|
+
get: (props: string | string[]) => {
|
|
460
|
+
validateStore();
|
|
461
|
+
return _get(STORE, props);
|
|
462
|
+
},
|
|
463
|
+
// watch store
|
|
464
|
+
addStoreListener: (listener: (data: Store) => {}) =>
|
|
465
|
+
(storeListener = listener),
|
|
466
|
+
removeStoreListener: () => (storeListener = undefined),
|
|
467
|
+
// watch single viewport
|
|
468
|
+
addViewportListener: (
|
|
469
|
+
elementId: string,
|
|
470
|
+
listener: (data: typeof DEFAULT_VIEWPORT) => {}
|
|
471
|
+
) => {
|
|
472
|
+
viewportsListeners[elementId] = listener;
|
|
473
|
+
},
|
|
474
|
+
removeViewportListener: (elementId: string) => {
|
|
475
|
+
delete viewportsListeners[elementId];
|
|
476
|
+
},
|
|
477
|
+
// watch single series
|
|
478
|
+
addSeriesListener: (
|
|
479
|
+
seriesId: string,
|
|
480
|
+
listener: (data: StoreSeries) => {}
|
|
481
|
+
) => {
|
|
482
|
+
seriesListeners[seriesId] = listener;
|
|
483
|
+
},
|
|
484
|
+
removeSeriesListener: (seriesId: string) => {
|
|
485
|
+
delete seriesListeners[seriesId];
|
|
486
|
+
}
|
|
487
|
+
};
|