larvitar 2.0.5 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +2 -2
  2. package/dist/imaging/imageRendering.d.ts +1 -71
  3. package/dist/imaging/imageStore.d.ts +5 -0
  4. package/dist/imaging/loaders/commonLoader.d.ts +4 -4
  5. package/dist/imaging/loaders/nrrdLoader.d.ts +1 -51
  6. package/dist/larvitar.js +13 -1
  7. package/dist/larvitar.js.map +1 -1
  8. package/imaging/tools/types.d.ts +19 -19
  9. package/imaging/types.d.ts +110 -2
  10. package/package.json +7 -2
  11. package/.github/workflows/build-docs.yml +0 -59
  12. package/.github/workflows/codeql-analysis.yml +0 -71
  13. package/.github/workflows/deploy.yml +0 -37
  14. package/.vscode/settings.json +0 -4
  15. package/CODE_OF_CONDUCT.md +0 -76
  16. package/MIGRATION.md +0 -25
  17. package/bundler/webpack.common.js +0 -27
  18. package/bundler/webpack.dev.js +0 -23
  19. package/bundler/webpack.prod.js +0 -19
  20. package/decs.d.ts +0 -12
  21. package/dist/imaging/MetaDataReadable.d.ts +0 -41
  22. package/dist/imaging/MetaDataTypes.d.ts +0 -3489
  23. package/imaging/dataDictionary.json +0 -21866
  24. package/imaging/imageAnonymization.ts +0 -135
  25. package/imaging/imageColormaps.ts +0 -217
  26. package/imaging/imageContours.ts +0 -196
  27. package/imaging/imageIo.ts +0 -251
  28. package/imaging/imageLayers.ts +0 -121
  29. package/imaging/imageLoading.ts +0 -299
  30. package/imaging/imageParsing.ts +0 -444
  31. package/imaging/imagePresets.ts +0 -156
  32. package/imaging/imageRendering.ts +0 -1091
  33. package/imaging/imageReslice.ts +0 -87
  34. package/imaging/imageStore.ts +0 -487
  35. package/imaging/imageTags.ts +0 -609
  36. package/imaging/imageTools.js +0 -708
  37. package/imaging/imageUtils.ts +0 -1079
  38. package/imaging/loaders/commonLoader.ts +0 -275
  39. package/imaging/loaders/dicomLoader.ts +0 -66
  40. package/imaging/loaders/fileLoader.ts +0 -71
  41. package/imaging/loaders/multiframeLoader.ts +0 -435
  42. package/imaging/loaders/nrrdLoader.ts +0 -630
  43. package/imaging/loaders/resliceLoader.ts +0 -205
  44. package/imaging/monitors/memory.ts +0 -151
  45. package/imaging/monitors/performance.ts +0 -34
  46. package/imaging/parsers/ecg.ts +0 -54
  47. package/imaging/parsers/nrrd.js +0 -485
  48. package/imaging/tools/custom/4dSliceScrollTool.js +0 -146
  49. package/imaging/tools/custom/BorderMagnifyTool.js +0 -99
  50. package/imaging/tools/custom/contourTool.js +0 -1884
  51. package/imaging/tools/custom/diameterTool.js +0 -141
  52. package/imaging/tools/custom/editMaskTool.js +0 -141
  53. package/imaging/tools/custom/ellipticalRoiOverlayTool.js +0 -534
  54. package/imaging/tools/custom/polygonSegmentationMixin.js +0 -245
  55. package/imaging/tools/custom/polylineScissorsTool.js +0 -59
  56. package/imaging/tools/custom/rectangleRoiOverlayTool.js +0 -564
  57. package/imaging/tools/custom/seedTool.js +0 -342
  58. package/imaging/tools/custom/setLabelMap3D.ts +0 -242
  59. package/imaging/tools/custom/thresholdsBrushTool.js +0 -161
  60. package/imaging/tools/default.ts +0 -594
  61. package/imaging/tools/interaction.ts +0 -266
  62. package/imaging/tools/io.ts +0 -229
  63. package/imaging/tools/main.ts +0 -427
  64. package/imaging/tools/segmentation.ts +0 -532
  65. package/imaging/tools/segmentations.md +0 -38
  66. package/imaging/tools/state.ts +0 -74
  67. package/imaging/tools/strategies/eraseFreehand.js +0 -76
  68. package/imaging/tools/strategies/fillFreehand.js +0 -79
  69. package/imaging/tools/strategies/index.js +0 -2
  70. package/imaging/waveforms/ecg.ts +0 -191
  71. package/index.ts +0 -431
  72. package/jsdoc.json +0 -52
  73. package/rollup.config.js +0 -51
  74. package/template/.gitkeep +0 -0
  75. package/tsconfig.json +0 -102
  76. /package/imaging/{MetaDataReadable.ts → MetaDataReadable.d.ts} +0 -0
  77. /package/imaging/{MetaDataTypes.ts → MetaDataTypes.d.ts} +0 -0
@@ -1,1091 +0,0 @@
1
- /** @module imaging/imageRendering
2
- * @desc This file provides functionalities for
3
- * rendering images in html canvas using cornerstone
4
- */
5
-
6
- // external libraries
7
- import cornerstone from "cornerstone-core";
8
- import { default as cornerstoneDICOMImageLoader } from "cornerstone-wado-image-loader";
9
- import { each, has } from "lodash";
10
-
11
- // internal libraries
12
- import { getPerformanceMonitor } from "./monitors/performance";
13
- import { getFileImageId } from "./loaders/fileLoader";
14
- import { csToolsCreateStack } from "./tools/main";
15
- import { toggleMouseToolsListeners } from "./tools/interaction";
16
- import store, { set as setStore } from "./imageStore";
17
- import { applyColorMap } from "./imageColormaps";
18
- import { isElement } from "./imageUtils";
19
- import {
20
- Image,
21
- Instance,
22
- Series,
23
- StoreViewport,
24
- StoreViewportOptions,
25
- Viewport
26
- } from "./types";
27
-
28
- /*
29
- * This module provides the following functions to be exported:
30
- * clearImageCache(seriesId)
31
- * loadAndCacheImage(imageIndex)
32
- * loadAndCacheImages(seriesData)
33
- * renderFileImage(file, elementId)
34
- * renderWebImage(url, elementId)
35
- * disableViewport(elementId)
36
- * unloadViewport(elementId, seriesId)
37
- * resizeViewport(elementId)
38
- * renderImage(series, elementId, defaultProps)
39
- * updateImage(series, elementId, imageIndex)
40
- * resetViewports([elementIds])
41
- * updateViewportData(elementId)
42
- * toggleMouseHandlers(elementId, disableFlag)
43
- * storeViewportData(params...)
44
- * invertImage(elementId)
45
- * flipImageHorizontal(elementId)
46
- * flipImageVertical(elementId)
47
- * rotateImageLeft(elementId)
48
- * rotateImageRight(elementId)
49
- */
50
-
51
- /**
52
- * Purge the cornestone internal cache
53
- * If seriesId is passed as argument only imageIds of the series are purged from internal cache
54
- * @instance
55
- * @function clearImageCache
56
- * @param {String} seriesId - The id of the serie
57
- */
58
- export const clearImageCache = function (seriesId?: string) {
59
- if (seriesId) {
60
- let series = store.get("series");
61
- if (has(series, seriesId)) {
62
- each(series[seriesId].imageIds, function (imageId: string) {
63
- if (cornerstone.imageCache.cachedImages.length > 0) {
64
- try {
65
- cornerstone.imageCache.removeImageLoadObject(imageId);
66
- } catch (e) {
67
- console.warn("no cached image");
68
- }
69
- } else {
70
- let uri =
71
- cornerstoneDICOMImageLoader.wadouri.parseImageId(imageId).url;
72
- cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.unload(uri);
73
- }
74
- });
75
-
76
- store.removeSeriesId(seriesId);
77
- console.log("Uncached images for ", seriesId);
78
- }
79
- } else {
80
- cornerstone.imageCache.purgeCache();
81
- }
82
- };
83
-
84
- /**
85
- * Load and cache a single image
86
- * Add series's imageIds into store
87
- * @instance
88
- * @function loadAndCacheImage
89
- * @param {Object} series the parsed series data
90
- * @param {number} imageIndex the image index in the imageIds array
91
- */
92
- export function loadAndCacheImage(
93
- series: Series,
94
- imageIndex: number
95
- ): Promise<true> {
96
- const t0 = performance.now();
97
- // add serie's imageIds into store
98
- store.addSeriesId(series.seriesUID, series.imageIds);
99
- const imageId: string | undefined = series.imageIds[imageIndex];
100
-
101
- const cachePromise = new Promise<true>((resolve, reject) => {
102
- if (imageId) {
103
- cornerstone.loadAndCacheImage(imageId).then(function () {
104
- const t1 = performance.now();
105
- console.log(`Call to cacheImages took ${t1 - t0} milliseconds.`);
106
- console.log(
107
- `Cached image with index ${imageIndex} for ${series.seriesUID}`
108
- );
109
- resolve(true);
110
- });
111
- } else {
112
- reject(`Error: wrong image index ${imageIndex}, no imageId available`);
113
- }
114
- });
115
- return cachePromise;
116
- }
117
-
118
- /**
119
- * Load and cache all serie's images
120
- * Add series's imageIds into store
121
- * @instance
122
- * @function loadAndCacheImages
123
- * @param {Object} series the parsed series data
124
- * @param {Function} callback a callback function
125
- */
126
- export function loadAndCacheImages(
127
- series: Series,
128
- callback: (payload: {
129
- seriesId: string;
130
- loading: number;
131
- series: Series;
132
- }) => any
133
- ) {
134
- const t0 = performance.now();
135
- let cachingCounter = 0;
136
- const response = {
137
- seriesId: series.seriesUID,
138
- loading: 0,
139
- series: {} as Series
140
- };
141
- callback(response);
142
- // add serie's imageIds into store
143
- store.addSeriesId(series.seriesUID, series.imageIds);
144
- // add serie's caching progress into store
145
- setStore(["progress", series.seriesUID, 0]);
146
-
147
- function updateProgress() {
148
- cachingCounter += 1;
149
- const cachingPercentage = Math.floor(
150
- (cachingCounter / series.imageIds.length) * 100
151
- );
152
- response.loading = cachingPercentage;
153
- setStore(["progress", series.seriesUID, cachingPercentage]);
154
- if (cachingCounter == series.imageIds.length) {
155
- const t1 = performance.now();
156
- console.log(`Call to cacheImages took ${t1 - t0} milliseconds.`);
157
- console.log(`Cached images for ${series.seriesUID}`);
158
- response.series = series;
159
- }
160
- }
161
-
162
- each(series.imageIds, function (imageId: string | undefined, index: number) {
163
- if (imageId) {
164
- cornerstone.loadAndCacheImage(imageId).then(function () {
165
- updateProgress();
166
- callback(response);
167
- });
168
- } else {
169
- updateProgress();
170
- console.warn(
171
- `Stack is not fully loaded, skipping cache for index ${index}`
172
- );
173
- callback(response);
174
- }
175
- });
176
- }
177
-
178
- /**
179
- * Render a PDF from a DICOM Encapsulated PDF
180
- * @instance
181
- * @function renderDICOMPDF
182
- * @param {Object} seriesStack - The original series data object
183
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
184
- * @returns {Promise} - Return a promise which will resolve when pdf is displayed
185
- */
186
- export const renderDICOMPDF = function (
187
- seriesStack: Series,
188
- elementId: string | HTMLElement
189
- ) {
190
- let t0 = performance.now();
191
- let element: HTMLElement | null = isElement(elementId)
192
- ? (elementId as HTMLElement)
193
- : document.getElementById(elementId as string);
194
-
195
- let renderPromise = new Promise<true>((resolve, reject) => {
196
- let image: Instance | null = seriesStack.instances[seriesStack.imageIds[0]];
197
- const SOPUID = image.dataSet?.string("x00080016");
198
-
199
- if (SOPUID === "1.2.840.10008.5.1.4.1.1.104.1") {
200
- let fileTag = image.dataSet?.elements.x00420011;
201
-
202
- if (!fileTag) {
203
- throw new Error("No file tag found");
204
- }
205
-
206
- let pdfByteArray = image.dataSet?.byteArray.slice(
207
- fileTag.dataOffset,
208
- fileTag.dataOffset + fileTag.length
209
- );
210
-
211
- if (!pdfByteArray) {
212
- console.error("No pdf byte array found");
213
- return;
214
- }
215
-
216
- if (!element) {
217
- console.error("invalid html element: " + elementId);
218
- return;
219
- }
220
-
221
- let PDF: Blob | null = new Blob([pdfByteArray], {
222
- type: "application/pdf"
223
- });
224
- let fileURL = URL.createObjectURL(PDF);
225
- element.innerHTML =
226
- '<object data="' +
227
- fileURL +
228
- '" type="application/pdf" width="100%" height="100%"></object>';
229
- const id: string = isElement(elementId)
230
- ? element.id
231
- : (elementId as string);
232
- setStore(["isPDF", id, true]);
233
- let t1 = performance.now();
234
- console.log(`Call to renderDICOMPDF took ${t1 - t0} milliseconds.`);
235
- image = null;
236
- fileTag = undefined;
237
- pdfByteArray = undefined;
238
- PDF = null;
239
- resolve(true);
240
- } else {
241
- reject("This is not a DICOM with a PDF");
242
- }
243
- });
244
- return renderPromise;
245
- };
246
-
247
- /**
248
- * Render an image (png or jpg) from File on a html div using cornerstone
249
- * @instance
250
- * @function renderFileImage
251
- * @param {Object} file - The image File object
252
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
253
- * @returns {Promise} - Return a promise which will resolve when image is displayed
254
- */
255
- export const renderFileImage = function (
256
- file: File,
257
- elementId: string | HTMLElement
258
- ) {
259
- let element = isElement(elementId)
260
- ? (elementId as HTMLElement)
261
- : document.getElementById(elementId as string);
262
-
263
- if (!element) {
264
- console.error("invalid html element: " + elementId);
265
- return;
266
- }
267
-
268
- if (cornerstone.getEnabledElements().length == 0) {
269
- cornerstone.enable(element);
270
- }
271
-
272
- let renderPromise = new Promise(resolve => {
273
- // check if imageId is already stored in fileManager
274
- const imageId = getFileImageId(file);
275
- if (imageId) {
276
- cornerstone.loadImage(imageId).then(function (image) {
277
- if (!element) {
278
- console.error("invalid html element: " + elementId);
279
- return;
280
- }
281
- cornerstone.displayImage(element, image);
282
- const viewport = cornerstone.getViewport(element) as Viewport;
283
-
284
- if (!viewport) {
285
- console.error("invalid viewport");
286
- return;
287
- }
288
-
289
- viewport.displayedArea.brhc.x = image.width;
290
- viewport.displayedArea.brhc.y = image.height;
291
- cornerstone.setViewport(element, viewport);
292
- cornerstone.fitToWindow(element);
293
- csToolsCreateStack(element);
294
- resolve(image);
295
- });
296
- }
297
- });
298
- return renderPromise;
299
- };
300
-
301
- /**
302
- * Render an image (png or jpg) from web url on a html div using cornerstone
303
- * @instance
304
- * @function renderWebImage
305
- * @param {String} url - The image data url
306
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
307
- * @returns {Promise} - Return a promise which will resolve when image is displayed
308
- */
309
- export const renderWebImage = function (
310
- url: string,
311
- elementId: string | HTMLElement
312
- ) {
313
- let element = isElement(elementId)
314
- ? (elementId as HTMLElement)
315
- : document.getElementById(elementId as string);
316
- let renderPromise = new Promise<cornerstone.Image>((resolve, reject) => {
317
- if (!element) {
318
- console.error("invalid html element: " + elementId);
319
- reject("invalid html element: " + elementId);
320
- return;
321
- }
322
- cornerstone.enable(element);
323
- cornerstone.loadImage(url).then(function (image) {
324
- if (!element) {
325
- console.error("invalid html element: " + elementId);
326
- reject("invalid html element: " + elementId);
327
- return;
328
- }
329
- cornerstone.displayImage(element, image);
330
- csToolsCreateStack(element);
331
- resolve(image);
332
- });
333
- });
334
- return renderPromise;
335
- };
336
-
337
- /**
338
- * Unrender an image on a html div using cornerstone
339
- * @instance
340
- * @function disableViewport
341
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
342
- */
343
- export const disableViewport = function (elementId: string | HTMLElement) {
344
- let element = isElement(elementId)
345
- ? (elementId as HTMLElement)
346
- : document.getElementById(elementId as string);
347
- if (!element) {
348
- console.error("invalid html element: " + elementId);
349
- return;
350
- }
351
- const id: string = isElement(elementId) ? element.id : (elementId as string);
352
- toggleMouseToolsListeners(id, true);
353
- cornerstone.disable(element);
354
- setStore(["ready", id, false]);
355
- };
356
-
357
- /**
358
- * Unrender an image on a html div using cornerstone
359
- * Remove image from cornerstone cache and remove from store
360
- * @instance
361
- * @function unloadViewport
362
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
363
- * @param {String} seriesId - The id of the serie
364
- */
365
- export const unloadViewport = function (elementId: string, seriesId: string) {
366
- disableViewport(elementId);
367
-
368
- if (!seriesId) {
369
- console.warn(
370
- "seriesId not provided, use disableViewport if you do not want to uncache images"
371
- );
372
- }
373
- // remove images from cornerstone cache
374
- if (seriesId && has(store.get("series"), seriesId)) {
375
- clearImageCache(seriesId);
376
- }
377
- store.deleteViewport(elementId);
378
- };
379
-
380
- /**
381
- * Resize a viewport using cornerstone resize
382
- * And forcing fit to window
383
- * @instance
384
- * @function resizeViewport
385
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
386
- */
387
- export const resizeViewport = function (elementId: string | HTMLElement) {
388
- let element = isElement(elementId)
389
- ? (elementId as HTMLElement)
390
- : document.getElementById(elementId as string);
391
- if (!element) {
392
- console.error("invalid html element: " + elementId);
393
- return;
394
- }
395
- cornerstone.resize(element, true); // true flag forces fitToWindow
396
- };
397
-
398
- /**
399
- * Cache image and render it in a html div using cornerstone
400
- * @instance
401
- * @function renderImage
402
- * @param {Object} seriesStack - The original series data object
403
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
404
- * @param {Object} defaultProps - Optional default props
405
- * @return {Promise} Return a promise which will resolve when image is displayed
406
- */
407
- export const renderImage = function (
408
- seriesStack: Series,
409
- elementId: string | HTMLElement,
410
- defaultProps: StoreViewportOptions
411
- ): Promise<true> {
412
- const t0 = performance.now();
413
- // get element and enable it
414
- const element = isElement(elementId)
415
- ? (elementId as HTMLElement)
416
- : document.getElementById(elementId as string);
417
- if (!element) {
418
- console.error("invalid html element: " + elementId);
419
- return new Promise((_, reject) =>
420
- reject("invalid html element: " + elementId)
421
- );
422
- }
423
- const id: string = isElement(elementId) ? element.id : (elementId as string);
424
- cornerstone.enable(element);
425
-
426
- setStore(["ready", id, false]);
427
-
428
- let series = { ...seriesStack };
429
- let data = getSeriesData(series, defaultProps);
430
- if (!data.imageId) {
431
- console.warn("error during renderImage: imageId has not been loaded yet.");
432
- return new Promise((_, reject) => {
433
- setStore(["pendingSliceId", id, data.imageIndex]);
434
- reject("error during renderImage: imageId has not been loaded yet.");
435
- });
436
- }
437
-
438
- const renderPromise = new Promise<true>((resolve, reject) => {
439
- // load and display one image (imageId)
440
- cornerstone.loadImage(data.imageId as string).then(function (image) {
441
- if (!element) {
442
- console.error("invalid html element: " + elementId);
443
- reject("invalid html element: " + elementId);
444
- return;
445
- }
446
-
447
- cornerstone.displayImage(element, image);
448
-
449
- if (series.layer) {
450
- // assign the image to its layer and return its id
451
- series.layer.id = cornerstone.addLayer(
452
- element,
453
- image,
454
- series.layer.options
455
- );
456
- }
457
-
458
- const viewport = cornerstone.getViewport(element);
459
-
460
- if (!viewport) {
461
- console.error("viewport not found");
462
- reject("viewport not found for element: " + elementId);
463
- return;
464
- }
465
-
466
- // window width and window level
467
- // are stored in specific dicom tags
468
- // (x00281050 and x00281051)
469
- // if not present check in image object
470
- if (data.viewport?.voi?.windowWidth === undefined) {
471
- data.viewport.voi.windowWidth = image.windowWidth;
472
- }
473
- if (data.viewport?.voi?.windowCenter === undefined) {
474
- data.viewport.voi.windowCenter = image.windowCenter;
475
- }
476
- if (data.default?.voi?.windowWidth === undefined) {
477
- data.default.voi.windowWidth = data.viewport.voi.windowWidth;
478
- }
479
- if (data.default?.voi?.windowCenter === undefined) {
480
- data.default.voi.windowCenter = data.viewport.voi.windowCenter;
481
- }
482
-
483
- cornerstone.fitToWindow(element);
484
-
485
- if (defaultProps && defaultProps.scale !== undefined) {
486
- viewport.scale = defaultProps["scale"];
487
- cornerstone.setViewport(element, viewport);
488
- }
489
-
490
- if (
491
- defaultProps &&
492
- defaultProps.tr_x !== undefined &&
493
- defaultProps.tr_y !== undefined
494
- ) {
495
- viewport.translation.x = defaultProps.tr_x;
496
- viewport.translation.y = defaultProps.tr_y;
497
- cornerstone.setViewport(element, viewport);
498
- }
499
-
500
- // color maps
501
- if (defaultProps && defaultProps.colormap && image.color == false) {
502
- applyColorMap(defaultProps["colormap"]);
503
- }
504
-
505
- const storedViewport = cornerstone.getViewport(element);
506
-
507
- if (!storedViewport) {
508
- console.error("storedViewport not found");
509
- reject("storedViewport not found for element: " + elementId);
510
- return;
511
- }
512
-
513
- storeViewportData(image, element.id, storedViewport as Viewport, data);
514
- setStore(["ready", element.id, true]);
515
- const t1 = performance.now();
516
- console.log(`Call to renderImage took ${t1 - t0} milliseconds.`);
517
-
518
- const uri = cornerstoneDICOMImageLoader.wadouri.parseImageId(
519
- data.imageId
520
- ).url;
521
- cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.unload(uri);
522
- //@ts-ignore
523
- image = null;
524
- //@ts-ignore
525
- series = null;
526
- //@ts-ignore
527
- data = null;
528
- resolve(true);
529
- });
530
- });
531
-
532
- csToolsCreateStack(element, series.imageIds, (data.imageIndex as number) - 1);
533
- toggleMouseToolsListeners(id, false);
534
-
535
- return renderPromise;
536
- };
537
-
538
- /**
539
- * Update the cornerstone image with new imageIndex
540
- * @instance
541
- * @function updateImage
542
- * @param {Object} series - The original series data object
543
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
544
- * @param {Number} imageIndex - The index of the image to be rendered
545
- * @param {Boolean} cacheImage - A flag to handle image cache
546
- */
547
- export const updateImage = async function (
548
- series: Series,
549
- elementId: string | HTMLElement,
550
- imageIndex: number,
551
- cacheImage: boolean
552
- ): Promise<void> {
553
- const element = isElement(elementId)
554
- ? (elementId as HTMLElement)
555
- : document.getElementById(elementId as string);
556
- if (!element) {
557
- throw "not element";
558
- }
559
-
560
- const id: string = isElement(elementId) ? element.id : (elementId as string);
561
- const imageId = series.imageIds[imageIndex];
562
- if (!imageId) {
563
- setStore(["pendingSliceId", id, imageIndex]);
564
- throw `Error: wrong image index ${imageIndex}, no imageId available`;
565
- }
566
-
567
- if (series.is4D) {
568
- const timestamp = series.instances[imageId].metadata.contentTime;
569
- const timeId =
570
- series.instances[imageId].metadata.temporalPositionIdentifier! - 1; // timeId from 0 to N
571
- setStore(["timeId", id as string, timeId]);
572
- setStore(["timestamp", id as string, timestamp]);
573
- }
574
-
575
- if (cacheImage) {
576
- let t0: number | undefined;
577
- if (getPerformanceMonitor() === true) {
578
- t0 = performance.now();
579
- }
580
-
581
- const image = await cornerstone.loadAndCacheImage(imageId);
582
- cornerstone.displayImage(element, image);
583
-
584
- if (getPerformanceMonitor() === true) {
585
- const t1 = performance.now();
586
- if (t0 !== undefined) {
587
- // check if t0 is defined before using it
588
- console.log(`Call to updateImage took ${t1 - t0} milliseconds.`);
589
- }
590
- }
591
-
592
- setStore(["sliceId", id, imageIndex]);
593
- const pendingSliceId = store.get(["viewports", id, "pendingSliceId"]);
594
- if (imageIndex == pendingSliceId) {
595
- setStore(["pendingSliceId", id, undefined]);
596
- }
597
- setStore(["minPixelValue", id, image.minPixelValue]);
598
- setStore(["maxPixelValue", id, image.maxPixelValue]);
599
- } else {
600
- let t0: number | undefined;
601
- if (getPerformanceMonitor() === true) {
602
- t0 = performance.now();
603
- }
604
-
605
- const image = await cornerstone.loadImage(imageId);
606
- cornerstone.displayImage(element, image);
607
-
608
- if (getPerformanceMonitor() === true) {
609
- const t1 = performance.now();
610
- if (t0 !== undefined) {
611
- // check if t0 is defined before using it
612
- console.log(`Call to updateImage took ${t1 - t0} milliseconds.`);
613
- }
614
- }
615
-
616
- setStore(["sliceId", id, imageIndex]);
617
- const pendingSliceId = store.get(["viewports", id, "pendingSliceId"]);
618
- if (imageIndex == pendingSliceId) {
619
- setStore(["pendingSliceId", id, undefined]);
620
- }
621
- setStore(["minPixelValue", id, image.minPixelValue]);
622
- setStore(["maxPixelValue", id, image.maxPixelValue]);
623
- }
624
- };
625
-
626
- /**
627
- * Reset viewport values (scale, translation and wwwc)
628
- * @instance
629
- * @function resetViewports
630
- * @param {Array} elementIds - The array of hmtl div ids
631
- * @param {Array} keys - The array of viewport sections to resets (default is all)
632
- */
633
- export const resetViewports = function (
634
- elementIds: string[],
635
- keys?: Array<
636
- "contrast" | "scaleAndTranslation" | "rotation" | "flip" | "zoom"
637
- >
638
- ) {
639
- each(elementIds, function (elementId: string) {
640
- const element = document.getElementById(elementId);
641
- if (!element) {
642
- console.error("invalid html element: " + elementId);
643
- return;
644
- }
645
-
646
- const defaultViewport = store.get(["viewports", elementId, "default"]);
647
- const viewport = cornerstone.getViewport(element);
648
-
649
- if (!viewport) {
650
- throw new Error("viewport not found");
651
- }
652
-
653
- if (!keys || keys.find(v => v === "contrast")) {
654
- viewport.voi.windowWidth = defaultViewport.voi.windowWidth;
655
- viewport.voi.windowCenter = defaultViewport.voi.windowCenter;
656
- viewport.invert = defaultViewport.voi.invert;
657
- setStore([
658
- "contrast",
659
- elementId,
660
- viewport.voi.windowWidth,
661
- viewport.voi.windowCenter
662
- ]);
663
- }
664
-
665
- if (!keys || keys.find(v => v === "scaleAndTranslation")) {
666
- viewport.scale = defaultViewport.scale;
667
- setStore(["scale", elementId, viewport.scale]);
668
-
669
- viewport.translation.x = defaultViewport.translation.x;
670
- viewport.translation.y = defaultViewport.translation.y;
671
- setStore([
672
- "translation",
673
- elementId,
674
- viewport.translation.x,
675
- viewport.translation.y
676
- ]);
677
- }
678
-
679
- if (!keys || keys.find(v => v === "rotation")) {
680
- viewport.rotation = defaultViewport.rotation;
681
- setStore(["rotation", elementId, viewport.rotation]);
682
- }
683
-
684
- if (!keys || keys.find(v => v === "flip")) {
685
- viewport.hflip = false;
686
- viewport.vflip = false;
687
- }
688
-
689
- if (!keys || keys.find(v => v === "zoom")) {
690
- viewport.scale = defaultViewport.scale;
691
- setStore(["scale", elementId, viewport.scale]);
692
- }
693
-
694
- cornerstone.setViewport(element, viewport);
695
-
696
- if (!keys || keys.find(v => v === "scaleAndTranslation")) {
697
- cornerstone.fitToWindow(element);
698
- }
699
- cornerstone.updateImage(element);
700
- });
701
- };
702
-
703
- /**
704
- * Update viewport data in store
705
- * @instance
706
- * @function updateViewportData
707
- * @param {String} elementId - The html div id used for rendering or its DOM HTMLElement
708
- * @param {Object} viewportData - The new viewport data
709
- */
710
- export const updateViewportData = function (
711
- elementId: string,
712
- viewportData: Viewport,
713
- activeTool: string
714
- ) {
715
- let element = document.getElementById(elementId as string);
716
- if (!element) {
717
- console.error("invalid html element: " + elementId);
718
- return;
719
- }
720
- // TODO: understand how to handle synchronized tools
721
- switch (activeTool) {
722
- case "Wwwc":
723
- case "WwwcRegion":
724
- if (viewportData.voi) {
725
- setStore([
726
- "contrast",
727
- elementId,
728
- viewportData.voi.windowWidth,
729
- viewportData.voi.windowCenter
730
- ]);
731
- }
732
- break;
733
- case "Pan":
734
- if (viewportData.translation) {
735
- setStore([
736
- "translation",
737
- elementId,
738
- viewportData.translation.x,
739
- viewportData.translation.y
740
- ]);
741
- }
742
- break;
743
- case "Zoom":
744
- if (viewportData.scale) {
745
- setStore(["scale", elementId, viewportData.scale]);
746
- }
747
- break;
748
- case "Rotate":
749
- if (viewportData.rotation) {
750
- setStore(["rotation", elementId, viewportData.rotation]);
751
- }
752
- break;
753
- case "mouseWheel":
754
- case "stackscroll":
755
- const viewport = store.get(["viewports", elementId]);
756
- const isTimeserie = viewport.isTimeserie;
757
- if (isTimeserie) {
758
- const index = viewportData.newImageIdIndex;
759
- const timeId = viewport.timeIds[index];
760
- const timestamp = viewport.timestamps[index];
761
- setStore(["timeId", elementId, timeId]);
762
- setStore(["timestamp", elementId, timestamp]);
763
- }
764
- break;
765
- default:
766
- break;
767
- }
768
- };
769
-
770
- /**
771
- * Store the viewport data into internal storage
772
- * @instance
773
- * @function storeViewportData
774
- * @param {Object} image - The cornerstone image frame
775
- * @param {String} elementId - The html div id used for rendering
776
- * @param {String} viewport - The viewport tag name
777
- * @param {Object} data - The viewport data object
778
- */
779
- export const storeViewportData = function (
780
- image: cornerstone.Image,
781
- elementId: string,
782
- viewport: Viewport,
783
- data: ReturnType<typeof getSeriesData>
784
- ) {
785
- setStore(["dimensions", elementId, data.rows, data.cols]);
786
- setStore(["spacing", elementId, data.spacing_x, data.spacing_y]);
787
- setStore(["thickness", elementId, data.thickness]);
788
- setStore(["minPixelValue", elementId, image.minPixelValue]);
789
- setStore(["maxPixelValue", elementId, image.maxPixelValue]);
790
- // slice id from 0 to n - 1
791
- setStore(["minSliceId", elementId, 0]);
792
- setStore(["sliceId", elementId, data.imageIndex]);
793
- const pendingSliceId = store.get(["viewports", elementId, "pendingSliceId"]);
794
- if (data.imageIndex == pendingSliceId) {
795
- setStore(["pendingSliceId", elementId, undefined]);
796
- }
797
- setStore(["maxSliceId", elementId, data.numberOfSlices - 1]);
798
-
799
- if (data.isTimeserie) {
800
- setStore(["minTimeId", elementId, 0]);
801
- setStore(["timeId", elementId, data.timeIndex || 0]);
802
- setStore(["maxTimeId", elementId, data.numberOfTemporalPositions - 1]);
803
- let maxSliceId = data.numberOfSlices * data.numberOfTemporalPositions - 1;
804
- setStore(["maxSliceId", elementId, maxSliceId]);
805
-
806
- setStore(["timestamp", elementId, data.timestamp]);
807
- setStore(["timestamps", elementId, data.timestamps]);
808
- setStore(["timeIds", elementId, data.timeIds]);
809
- } else {
810
- setStore(["minTimeId", elementId, 0]);
811
- setStore(["timeId", elementId, 0]);
812
- setStore(["maxTimeId", elementId, 0]);
813
- setStore(["timestamp", elementId, 0]);
814
- setStore(["timestamps", elementId, []]);
815
- setStore(["timeIds", elementId, []]);
816
- }
817
-
818
- setStore([
819
- "defaultViewport",
820
- elementId,
821
- viewport.scale || 0,
822
- viewport.rotation || 0,
823
- viewport.translation?.x || 0,
824
- viewport.translation?.y || 0,
825
- data.default?.voi?.windowWidth,
826
- data.default?.voi?.windowCenter,
827
- viewport.invert === true
828
- ]);
829
- setStore(["scale", elementId, viewport.scale || 0]);
830
- setStore(["rotation", elementId, viewport.rotation || 0]);
831
-
832
- setStore([
833
- "translation",
834
- elementId,
835
- viewport.translation?.x || 0,
836
- viewport.translation?.y || 0
837
- ]);
838
- setStore([
839
- "contrast",
840
- elementId,
841
- viewport.voi?.windowWidth || 0,
842
- viewport.voi?.windowCenter || 0
843
- ]);
844
- setStore(["isColor", elementId, data.isColor]);
845
- setStore(["isMultiframe", elementId, data.isMultiframe]);
846
- setStore(["isTimeserie", elementId, data.isTimeserie]);
847
- setStore(["isPDF", elementId, false]);
848
- };
849
-
850
- /**
851
- * Invert pixels of an image
852
- * @instance
853
- * @function invertImage
854
- * @param {Object} elementId - The html div id used for rendering or its DOM HTMLElement
855
- */
856
- export const invertImage = function (elementId: string | HTMLElement) {
857
- let element = isElement(elementId)
858
- ? (elementId as HTMLElement)
859
- : document.getElementById(elementId as string);
860
- if (!element) {
861
- console.error("invalid html element: " + elementId);
862
- return;
863
- }
864
- let viewport = cornerstone.getViewport(element);
865
-
866
- if (!viewport) {
867
- throw new Error("Viewport is undefined");
868
- }
869
-
870
- viewport.invert = !viewport.invert;
871
- cornerstone.setViewport(element, viewport);
872
- };
873
-
874
- /**
875
- * Flip image around horizontal axis
876
- * @instance
877
- * @function flipImageHorizontal
878
- * @param {Object} elementId - The html div id used for rendering or its DOM HTMLElement
879
- */
880
- export const flipImageHorizontal = function (elementId: string | HTMLElement) {
881
- let element = isElement(elementId)
882
- ? (elementId as HTMLElement)
883
- : document.getElementById(elementId as string);
884
- if (!element) {
885
- console.error("invalid html element: " + elementId);
886
- return;
887
- }
888
- let viewport = cornerstone.getViewport(element);
889
-
890
- if (!viewport) {
891
- throw new Error("Viewport is undefined");
892
- }
893
-
894
- viewport.hflip = !viewport.hflip;
895
- cornerstone.setViewport(element, viewport);
896
- };
897
-
898
- /**
899
- * Flip image around vertical axis
900
- * @instance
901
- * @function flipImageVertical
902
- * @param {Object} elementId - The html div id used for rendering or its DOM HTMLElement
903
- */
904
- export const flipImageVertical = function (elementId: string | HTMLElement) {
905
- let element = isElement(elementId)
906
- ? (elementId as HTMLElement)
907
- : document.getElementById(elementId as string);
908
- if (!element) {
909
- console.error("invalid html element: " + elementId);
910
- return;
911
- }
912
- let viewport = cornerstone.getViewport(element);
913
-
914
- if (!viewport) {
915
- throw new Error("Viewport is undefined");
916
- }
917
-
918
- viewport.vflip = !viewport.vflip;
919
- cornerstone.setViewport(element, viewport);
920
- };
921
-
922
- /**
923
- * Rotate image by 90° in left direction
924
- * @instance
925
- * @function rotateImageLeft
926
- * @param {Object} elementId - The html div id used for rendering or its DOM HTMLElement
927
- */
928
- export const rotateImageLeft = function (elementId: string | HTMLElement) {
929
- let element = isElement(elementId)
930
- ? (elementId as HTMLElement)
931
- : document.getElementById(elementId as string);
932
- if (!element) {
933
- console.error("invalid html element: " + elementId);
934
- return;
935
- }
936
- let viewport = cornerstone.getViewport(element);
937
-
938
- if (!viewport) {
939
- throw new Error("Viewport is undefined");
940
- }
941
-
942
- viewport.rotation -= 90;
943
- cornerstone.setViewport(element, viewport);
944
- };
945
-
946
- /**
947
- * Rotate image by 90° in right direction
948
- * @instance
949
- * @function rotateImageRight
950
- * @param {Object} elementId - The html div id used for rendering or its DOM HTMLElement
951
- */
952
- export const rotateImageRight = function (elementId: string | HTMLElement) {
953
- let element = isElement(elementId)
954
- ? (elementId as HTMLElement)
955
- : document.getElementById(elementId as string);
956
- if (!element) {
957
- console.error("invalid html element: " + elementId);
958
- return;
959
- }
960
- let viewport = cornerstone.getViewport(element);
961
-
962
- if (!viewport) {
963
- throw new Error("Viewport is undefined");
964
- }
965
-
966
- viewport.rotation += 90;
967
- cornerstone.setViewport(element, viewport);
968
- };
969
-
970
- /* Internal module functions */
971
-
972
- /**
973
- * Get series metadata from default props and series' metadata
974
- * @instance
975
- * @function getSeriesData
976
- * @param {Object} series - The parsed data series
977
- * @param {Object} defaultProps - Optional default properties
978
- * @return {Object} data - A data dictionary with parsed tags' values
979
- */
980
- const getSeriesData = function (
981
- series: Series,
982
- defaultProps: StoreViewportOptions
983
- ) {
984
- type RecursivePartial<T> = {
985
- [P in keyof T]?: RecursivePartial<T[P]>;
986
- };
987
- type SeriesData = StoreViewport & {
988
- imageIndex: number;
989
- imageId: string;
990
- numberOfSlices: number;
991
- numberOfTemporalPositions: number;
992
- timeIndex?: number;
993
- };
994
- const data: RecursivePartial<SeriesData> = {};
995
-
996
- if (series.isMultiframe) {
997
- data.isMultiframe = true;
998
- data.numberOfSlices = series.imageIds.length;
999
- data.imageIndex = 0;
1000
- data.imageId = series.imageIds[data.imageIndex];
1001
- data.isTimeserie = false;
1002
- } else if (series.is4D) {
1003
- data.isMultiframe = false;
1004
- data.isTimeserie = true;
1005
- // check with real indices
1006
- data.numberOfSlices = series.numberOfImages;
1007
- data.numberOfTemporalPositions = series.numberOfTemporalPositions;
1008
- data.imageIndex = 0;
1009
- data.timeIndex = 0;
1010
- data.imageId = series.imageIds[data.imageIndex];
1011
- data.timestamp = series.instances[data.imageId].metadata[
1012
- "x00080033"
1013
- ] as number;
1014
- data.timestamps = [];
1015
- data.timeIds = [];
1016
- each(series.imageIds, function (imageId: string) {
1017
- (data.timestamps as any[]).push(
1018
- series.instances[imageId].metadata.contentTime
1019
- );
1020
- (data.timeIds as any[]).push(
1021
- series.instances[imageId].metadata.temporalPositionIdentifier! - 1 // timeId from 0 to N
1022
- );
1023
- });
1024
- } else {
1025
- data.isMultiframe = false;
1026
- data.isTimeserie = false;
1027
- const numberOfSlices =
1028
- defaultProps && defaultProps.numberOfSlices
1029
- ? defaultProps.numberOfSlices
1030
- : series.imageIds.length;
1031
- data.numberOfSlices = numberOfSlices;
1032
- data.imageIndex =
1033
- defaultProps?.sliceNumber !== undefined && defaultProps?.sliceNumber >= 0 // slice number between 0 and n-1
1034
- ? defaultProps.sliceNumber
1035
- : Math.floor(numberOfSlices / 2);
1036
-
1037
- data.imageId = series.imageIds[data.imageIndex];
1038
- }
1039
- const instance: Instance | null = data.imageId
1040
- ? series.instances[data.imageId]
1041
- : null;
1042
-
1043
- data.isColor = series.color as boolean;
1044
- data.isPDF = series.isPDF;
1045
- if (instance) {
1046
- data.rows = instance.metadata.x00280010!;
1047
- data.cols = instance.metadata.x00280011!;
1048
- data.thickness = instance.metadata.x00180050 as number;
1049
-
1050
- let spacing = instance.metadata.x00280030!;
1051
- data.spacing_x = spacing ? spacing[0] : 1;
1052
- data.spacing_y = spacing ? spacing[1] : 1;
1053
- // window center and window width
1054
- data.viewport = {
1055
- voi: {
1056
- windowCenter:
1057
- defaultProps && defaultProps.wc
1058
- ? defaultProps.wc
1059
- : (instance.metadata.x00281050 as number),
1060
- windowWidth:
1061
- defaultProps && defaultProps.ww
1062
- ? defaultProps.ww
1063
- : (instance.metadata.x00281051 as number)
1064
- }
1065
- };
1066
- data.default = {
1067
- voi: {
1068
- windowCenter:
1069
- defaultProps && has(defaultProps, "defaultWC")
1070
- ? defaultProps.defaultWC
1071
- : data.viewport!.voi!.windowCenter,
1072
- windowWidth:
1073
- defaultProps && has(defaultProps, "defaultWW")
1074
- ? defaultProps.defaultWW
1075
- : data.viewport!.voi!.windowWidth
1076
- }
1077
- };
1078
- if (data.rows == null || data.cols == null) {
1079
- console.warn("invalid image metadata (rows or cols is null)");
1080
- setStore(["errorLog", "Invalid Image Metadata"]);
1081
- } else {
1082
- setStore(["errorLog", ""]);
1083
- }
1084
- } else {
1085
- console.warn(
1086
- `ImageId not found in imageIds with index ${data.imageIndex}.`
1087
- );
1088
- }
1089
-
1090
- return data as SeriesData;
1091
- };