@cornerstonejs/core 2.3.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,16 +2,16 @@ import { vec3 } from 'gl-matrix';
2
2
  import cache from '../cache/cache';
3
3
  import RLEVoxelMap from './RLEVoxelMap';
4
4
  import isEqual from './isEqual';
5
+ import { iterateOverPointsInShapeVoxelManager } from './pointInShapeCallback';
5
6
  const DEFAULT_RLE_SIZE = 5 * 1024;
6
7
  export default class VoxelManager {
7
- constructor(dimensions, _get, _set) {
8
+ constructor(dimensions, options) {
8
9
  this.modifiedSlices = new Set();
9
10
  this.boundsIJK = [
10
11
  [Infinity, -Infinity],
11
12
  [Infinity, -Infinity],
12
13
  [Infinity, -Infinity],
13
14
  ];
14
- this.numberOfComponents = 1;
15
15
  this.scalarData = null;
16
16
  this._sliceDataCache = null;
17
17
  this.getAtIJK = (i, j, k) => {
@@ -61,51 +61,23 @@ export default class VoxelManager {
61
61
  const kMax = Math.max(isInObjectBoundsIJK[2][0], isInObjectBoundsIJK[2][1]);
62
62
  const pointsInShape = [];
63
63
  if (useLPSTransform) {
64
- const { imageData } = options;
65
- const direction = imageData.getDirection();
66
- const rowCosines = direction.slice(0, 3);
67
- const columnCosines = direction.slice(3, 6);
68
- const scanAxisNormal = direction.slice(6, 9);
69
- const spacing = imageData.getSpacing();
70
- const [rowSpacing, columnSpacing, scanAxisSpacing] = spacing;
71
- const start = vec3.fromValues(iMin, jMin, kMin);
72
- const worldPosStart = imageData.indexToWorld(start);
73
- const rowStep = vec3.fromValues(rowCosines[0] * rowSpacing, rowCosines[1] * rowSpacing, rowCosines[2] * rowSpacing);
74
- const columnStep = vec3.fromValues(columnCosines[0] * columnSpacing, columnCosines[1] * columnSpacing, columnCosines[2] * columnSpacing);
75
- const scanAxisStep = vec3.fromValues(scanAxisNormal[0] * scanAxisSpacing, scanAxisNormal[1] * scanAxisSpacing, scanAxisNormal[2] * scanAxisSpacing);
76
- const currentPos = vec3.clone(worldPosStart);
77
- for (let k = kMin; k <= kMax; k++) {
78
- const startPosJ = vec3.clone(currentPos);
79
- for (let j = jMin; j <= jMax; j++) {
80
- const startPosI = vec3.clone(currentPos);
81
- for (let i = iMin; i <= iMax; i++) {
82
- const pointIJK = [i, j, k];
83
- if (isInObject(currentPos, pointIJK)) {
84
- const index = this.toIndex(pointIJK);
85
- const value = this._get(index);
86
- if (returnPoints) {
87
- pointsInShape.push({
88
- value,
89
- index,
90
- pointIJK,
91
- pointLPS: currentPos.slice(),
92
- });
93
- }
94
- if (callback) {
95
- callback({ value, index, pointIJK, pointLPS: currentPos });
96
- }
97
- }
98
- vec3.add(currentPos, currentPos, rowStep);
99
- }
100
- vec3.copy(currentPos, startPosI);
101
- vec3.add(currentPos, currentPos, columnStep);
102
- }
103
- vec3.copy(currentPos, startPosJ);
104
- vec3.add(currentPos, currentPos, scanAxisStep);
105
- }
64
+ const pointsInShape = iterateOverPointsInShapeVoxelManager({
65
+ voxelManager: this,
66
+ imageData: options.imageData,
67
+ bounds: [
68
+ [iMin, iMax],
69
+ [jMin, jMax],
70
+ [kMin, kMax],
71
+ ],
72
+ pointInShapeFn: isInObject,
73
+ callback,
74
+ });
106
75
  return pointsInShape;
107
76
  }
108
77
  if (this.map) {
78
+ if (this.map instanceof RLEVoxelMap) {
79
+ return this.rleForEach(callback, options);
80
+ }
109
81
  for (const index of this.map.keys()) {
110
82
  const pointIJK = this.toIJK(index);
111
83
  if (!isInObject(null, pointIJK)) {
@@ -199,8 +171,36 @@ export default class VoxelManager {
199
171
  this.dimensions = dimensions;
200
172
  this.width = dimensions[0];
201
173
  this.frameSize = this.width * dimensions[1];
202
- this._get = _get;
203
- this._set = _set;
174
+ this._get = options._get;
175
+ this._set = options._set;
176
+ this._id = options._id || '';
177
+ this._getConstructor = options._getConstructor;
178
+ this.numberOfComponents = this.numberOfComponents || 1;
179
+ this.scalarData = options.scalarData;
180
+ this._getScalarData = options._getScalarData;
181
+ this._updateScalarData = options._updateScalarData;
182
+ }
183
+ getMinMax() {
184
+ let min, max;
185
+ const callback = ({ value: v }) => {
186
+ const isArray = Array.isArray(v);
187
+ if (min === undefined) {
188
+ min = isArray ? [...v] : v;
189
+ max = isArray ? [...v] : v;
190
+ }
191
+ if (isArray) {
192
+ for (let i = 0; i < v.length; i++) {
193
+ min[i] = Math.min(min[i], v[i]);
194
+ max[i] = Math.max(max[i], v[i]);
195
+ }
196
+ }
197
+ else {
198
+ min = Math.min(min, v);
199
+ max = Math.max(max, v);
200
+ }
201
+ };
202
+ this.forEach(callback, { boundsIJK: this.getDefaultBounds() });
203
+ return { min, max };
204
204
  }
205
205
  toIJK(index) {
206
206
  return [
@@ -221,12 +221,46 @@ export default class VoxelManager {
221
221
  }
222
222
  return this.getDefaultBounds();
223
223
  }
224
- getScalarData() {
224
+ rleForEach(callback, options) {
225
+ const boundsIJK = options?.boundsIJK || this.getBoundsIJK();
226
+ const { isWithinObject } = options || {};
227
+ const map = this.map;
228
+ map.defaultValue = undefined;
229
+ for (let k = boundsIJK[2][0]; k <= boundsIJK[2][1]; k++) {
230
+ for (let j = boundsIJK[1][0]; j <= boundsIJK[1][1]; j++) {
231
+ const row = map.getRun(j, k);
232
+ if (!row) {
233
+ continue;
234
+ }
235
+ for (const rle of row) {
236
+ const { start, end, value } = rle;
237
+ const baseIndex = this.toIndex([0, j, k]);
238
+ for (let i = start; i < end; i++) {
239
+ const callbackArguments = {
240
+ value,
241
+ index: baseIndex + i,
242
+ pointIJK: [i, j, k],
243
+ };
244
+ if (isWithinObject?.(callbackArguments) === false) {
245
+ continue;
246
+ }
247
+ callback(callbackArguments);
248
+ }
249
+ }
250
+ }
251
+ }
252
+ }
253
+ getScalarData(storeScalarData = false) {
225
254
  if (this.scalarData) {
255
+ this._updateScalarData?.(this.scalarData);
226
256
  return this.scalarData;
227
257
  }
228
258
  if (this._getScalarData) {
229
- return this._getScalarData();
259
+ const scalarData = this._getScalarData();
260
+ if (storeScalarData) {
261
+ console.log('Not transient, should store value', scalarData);
262
+ }
263
+ return scalarData;
230
264
  }
231
265
  throw new Error('No scalar data available');
232
266
  }
@@ -328,25 +362,36 @@ export default class VoxelManager {
328
362
  return 0;
329
363
  }
330
364
  static _createRGBScalarVolumeVoxelManager({ dimensions, scalarData, numberOfComponents = 3, }) {
331
- const voxels = new VoxelManager(dimensions, (index) => {
332
- index *= numberOfComponents;
333
- return [scalarData[index++], scalarData[index++], scalarData[index++]];
334
- }, (index, v) => {
335
- index *= 3;
336
- const isChanged = !isEqual(scalarData[index], v);
337
- scalarData[index++] = v[0];
338
- scalarData[index++] = v[1];
339
- scalarData[index++] = v[2];
340
- return isChanged;
365
+ const voxels = new VoxelManager(dimensions, {
366
+ _get: (index) => {
367
+ index *= numberOfComponents;
368
+ return [
369
+ scalarData[index++],
370
+ scalarData[index++],
371
+ scalarData[index++],
372
+ ];
373
+ },
374
+ _id: '_createRGBScalarVolumeVoxelManager',
375
+ _set: (index, v) => {
376
+ index *= 3;
377
+ const isChanged = !isEqual(scalarData[index], v);
378
+ scalarData[index++] = v[0];
379
+ scalarData[index++] = v[1];
380
+ scalarData[index++] = v[2];
381
+ return isChanged;
382
+ },
383
+ numberOfComponents,
384
+ scalarData,
341
385
  });
342
- voxels.numberOfComponents = numberOfComponents;
343
- voxels.scalarData = scalarData;
344
386
  return voxels;
345
387
  }
346
388
  static createImageVolumeVoxelManager({ dimensions, imageIds, numberOfComponents = 1, }) {
347
389
  const pixelsPerSlice = dimensions[0] * dimensions[1];
348
390
  function getPixelInfo(index) {
349
391
  const sliceIndex = Math.floor(index / pixelsPerSlice);
392
+ if (sliceIndex < 0 || sliceIndex >= dimensions[2]) {
393
+ return {};
394
+ }
350
395
  const imageId = imageIds[sliceIndex];
351
396
  if (!imageId) {
352
397
  console.warn(`ImageId not found for sliceIndex: ${sliceIndex}`);
@@ -357,58 +402,44 @@ export default class VoxelManager {
357
402
  console.warn(`Image not found for imageId: ${imageId}`);
358
403
  return { pixelData: null, pixelIndex: null };
359
404
  }
360
- const pixelData = image.voxelManager.getScalarData();
361
- const pixelIndex = (index % pixelsPerSlice) * numberOfComponents;
362
- return { pixelData, pixelIndex };
405
+ const voxelManager = image.voxelManager;
406
+ const pixelIndex = index % pixelsPerSlice;
407
+ return { voxelManager, pixelIndex };
363
408
  }
364
409
  function getVoxelValue(index) {
365
- const { pixelData, pixelIndex } = getPixelInfo(index);
366
- if (!pixelData || pixelIndex === null) {
410
+ const { voxelManager: imageVoxelManager, pixelIndex } = getPixelInfo(index);
411
+ if (!imageVoxelManager || pixelIndex === null) {
367
412
  return null;
368
413
  }
369
- if (numberOfComponents === 1) {
370
- return pixelData[pixelIndex];
371
- }
372
- else {
373
- return [
374
- pixelData[pixelIndex],
375
- pixelData[pixelIndex + 1],
376
- pixelData[pixelIndex + 2],
377
- ];
378
- }
414
+ return imageVoxelManager.getAtIndex(pixelIndex);
379
415
  }
380
416
  function setVoxelValue(index, v) {
381
- const { pixelData, pixelIndex } = getPixelInfo(index);
382
- if (!pixelData || pixelIndex === null) {
417
+ const { voxelManager: imageVoxelManager, pixelIndex } = getPixelInfo(index);
418
+ if (!imageVoxelManager || pixelIndex === null) {
383
419
  return false;
384
420
  }
385
- let isChanged = false;
386
- if (numberOfComponents === 1) {
387
- if (pixelData[pixelIndex] !== v) {
388
- pixelData[pixelIndex] = v;
389
- isChanged = true;
390
- }
391
- }
392
- else {
393
- const rgbValue = v;
394
- for (let i = 0; i < numberOfComponents; i++) {
395
- if (pixelData[pixelIndex + i] !== rgbValue[i]) {
396
- pixelData[pixelIndex + i] = rgbValue[i];
397
- isChanged = true;
398
- }
399
- }
421
+ const currentValue = imageVoxelManager.getAtIndex(pixelIndex);
422
+ const isChanged = !isEqual(v, currentValue);
423
+ if (!isChanged) {
424
+ return isChanged;
400
425
  }
401
- return isChanged;
426
+ imageVoxelManager.setAtIndex(pixelIndex, v);
427
+ return true;
402
428
  }
403
- const voxelManager = new VoxelManager(dimensions, (index) => getVoxelValue(index), (index, v) => setVoxelValue(index, v));
404
- voxelManager.numberOfComponents = numberOfComponents;
405
- voxelManager._getConstructor = () => {
429
+ const _getConstructor = () => {
406
430
  const pixelInfo = getPixelInfo(0);
407
- if (!pixelInfo.pixelData) {
431
+ if (!pixelInfo?.pixelData) {
408
432
  return null;
409
433
  }
410
434
  return pixelInfo.pixelData.constructor;
411
435
  };
436
+ const voxelManager = new VoxelManager(dimensions, {
437
+ _get: getVoxelValue,
438
+ _set: setVoxelValue,
439
+ numberOfComponents,
440
+ _getConstructor,
441
+ _id: 'createImageVolumeVoxelManager',
442
+ });
412
443
  voxelManager.getMiddleSliceData = () => {
413
444
  const middleSliceIndex = Math.floor(dimensions[2] / 2);
414
445
  return voxelManager.getSliceData({
@@ -543,8 +574,12 @@ export default class VoxelManager {
543
574
  numberOfComponents,
544
575
  });
545
576
  });
546
- const voxelManager = new VoxelManager(dimensions, (index) => voxelGroups[timePoint]._get(index), (index, v) => voxelGroups[timePoint]._set(index, v));
547
- voxelManager.numberOfComponents = numberOfComponents;
577
+ const voxelManager = new VoxelManager(dimensions, {
578
+ _get: (index) => voxelGroups[timePoint]._get(index),
579
+ _set: (index, v) => voxelGroups[timePoint]._set(index, v),
580
+ numberOfComponents,
581
+ _id: 'createScalarDynamicVolumeVoxelManager',
582
+ });
548
583
  voxelManager.getScalarDataLength = () => {
549
584
  return voxelGroups[timePoint].getScalarDataLength();
550
585
  };
@@ -599,10 +634,14 @@ export default class VoxelManager {
599
634
  });
600
635
  }
601
636
  static _createNumberVolumeVoxelManager({ dimensions, scalarData, }) {
602
- const voxels = new VoxelManager(dimensions, (index) => scalarData[index], (index, v) => {
603
- const isChanged = scalarData[index] !== v;
604
- scalarData[index] = v;
605
- return isChanged;
637
+ const voxels = new VoxelManager(dimensions, {
638
+ _get: (index) => scalarData[index],
639
+ _set: (index, v) => {
640
+ const isChanged = scalarData[index] !== v;
641
+ scalarData[index] = v;
642
+ return isChanged;
643
+ },
644
+ _id: '_createNumberVolumeVoxelManager',
606
645
  });
607
646
  voxels.scalarData = scalarData;
608
647
  voxels.getMiddleSliceData = () => {
@@ -616,59 +655,117 @@ export default class VoxelManager {
616
655
  }
617
656
  static createMapVoxelManager({ dimension, }) {
618
657
  const map = new Map();
619
- const voxelManager = new VoxelManager(dimension, map.get.bind(map), (index, v) => map.set(index, v) && true);
658
+ const voxelManager = new VoxelManager(dimension, {
659
+ _get: map.get.bind(map),
660
+ _set: (index, v) => map.set(index, v) && true,
661
+ _id: 'createMapVoxelManager',
662
+ });
620
663
  voxelManager.map = map;
621
664
  return voxelManager;
622
665
  }
623
666
  static createHistoryVoxelManager({ sourceVoxelManager, }) {
624
667
  const map = new Map();
625
668
  const { dimensions } = sourceVoxelManager;
626
- const voxelManager = new VoxelManager(dimensions, (index) => map.get(index), function (index, v) {
627
- if (!map.has(index)) {
628
- const oldV = this.sourceVoxelManager.getAtIndex(index);
629
- if (oldV === v) {
630
- return false;
669
+ const voxelManager = new VoxelManager(dimensions, {
670
+ _get: (index) => map.get(index),
671
+ _set: function (index, v) {
672
+ if (!map.has(index)) {
673
+ const oldV = this.sourceVoxelManager.getAtIndex(index);
674
+ if (oldV === v) {
675
+ return false;
676
+ }
677
+ map.set(index, oldV);
631
678
  }
632
- map.set(index, oldV);
633
- }
634
- else if (v === map.get(index)) {
635
- map.delete(index);
636
- }
637
- this.sourceVoxelManager.setAtIndex(index, v);
679
+ else if (v === map.get(index)) {
680
+ map.delete(index);
681
+ }
682
+ this.sourceVoxelManager.setAtIndex(index, v);
683
+ },
684
+ _id: 'createHistoryVoxelManager',
638
685
  });
639
686
  voxelManager.map = map;
640
687
  voxelManager.scalarData = sourceVoxelManager.scalarData;
641
688
  voxelManager.sourceVoxelManager = sourceVoxelManager;
642
689
  return voxelManager;
643
690
  }
691
+ static createRLEHistoryVoxelManager(sourceVoxelManager) {
692
+ const { dimensions } = sourceVoxelManager;
693
+ const map = new RLEVoxelMap(dimensions[0], dimensions[1], dimensions[2]);
694
+ const voxelManager = new VoxelManager(dimensions, {
695
+ _get: (index) => map.get(index),
696
+ _set: function (index, v) {
697
+ const originalV = map.get(index);
698
+ if (originalV === undefined) {
699
+ const oldV = this.sourceVoxelManager.getAtIndex(index);
700
+ if (oldV === v || (oldV === undefined && v === 0) || v === null) {
701
+ return false;
702
+ }
703
+ map.set(index, oldV ?? 0);
704
+ }
705
+ else if (v === originalV || v === null) {
706
+ map.delete(index);
707
+ v = originalV;
708
+ }
709
+ this.sourceVoxelManager.setAtIndex(index, v);
710
+ },
711
+ _getScalarData: RLEVoxelMap.getScalarData,
712
+ _updateScalarData: (scalarData) => {
713
+ map.updateScalarData(scalarData);
714
+ return scalarData;
715
+ },
716
+ _id: 'createRLEHistoryVoxelManager',
717
+ });
718
+ voxelManager.map = map;
719
+ voxelManager.sourceVoxelManager = sourceVoxelManager;
720
+ return voxelManager;
721
+ }
644
722
  static createLazyVoxelManager({ dimensions, planeFactory, }) {
645
723
  const map = new Map();
646
724
  const [width, height] = dimensions;
647
725
  const planeSize = width * height;
648
- const voxelManager = new VoxelManager(dimensions, (index) => map.get(Math.floor(index / planeSize))[index % planeSize], (index, v) => {
649
- const k = Math.floor(index / planeSize);
650
- let layer = map.get(k);
651
- if (!layer) {
652
- layer = planeFactory(width, height);
653
- map.set(k, layer);
654
- }
655
- layer[index % planeSize] = v;
656
- return true;
726
+ const voxelManager = new VoxelManager(dimensions, {
727
+ _get: (index) => map.get(Math.floor(index / planeSize))[index % planeSize],
728
+ _set: (index, v) => {
729
+ const k = Math.floor(index / planeSize);
730
+ let layer = map.get(k);
731
+ if (!layer) {
732
+ layer = planeFactory(width, height);
733
+ map.set(k, layer);
734
+ }
735
+ layer[index % planeSize] = v;
736
+ return true;
737
+ },
738
+ _id: 'createLazyVoxelManager',
657
739
  });
658
740
  voxelManager.map = map;
659
741
  return voxelManager;
660
742
  }
661
- static createRLEVoxelManager({ dimensions, }) {
743
+ static createRLEVolumeVoxelManager({ dimensions, }) {
662
744
  const [width, height, depth] = dimensions;
663
745
  const map = new RLEVoxelMap(width, height, depth);
664
- const voxelManager = new VoxelManager(dimensions, (index) => map.get(index), (index, v) => {
665
- map.set(index, v);
666
- return true;
746
+ const voxelManager = new VoxelManager(dimensions, {
747
+ _get: (index) => map.get(index),
748
+ _set: (index, v) => {
749
+ map.set(index, v);
750
+ return true;
751
+ },
752
+ _getScalarData: RLEVoxelMap.getScalarData,
753
+ _updateScalarData: (scalarData) => {
754
+ map.updateScalarData(scalarData);
755
+ return scalarData;
756
+ },
757
+ _id: 'createRLEVolumeVoxelManager',
667
758
  });
668
759
  voxelManager.map = map;
669
760
  voxelManager.getPixelData = map.getPixelData.bind(map);
670
761
  return voxelManager;
671
762
  }
763
+ static createRLEImageVoxelManager({ dimensions, }) {
764
+ const [width, height] = dimensions;
765
+ return VoxelManager.createRLEVolumeVoxelManager({
766
+ dimensions: [width, height, 1],
767
+ });
768
+ }
672
769
  static addInstanceToImage(image) {
673
770
  const { width, height } = image;
674
771
  const scalarData = image.voxelManager.getScalarData();
@@ -679,7 +776,7 @@ export default class VoxelManager {
679
776
  });
680
777
  return;
681
778
  }
682
- image.voxelManager = VoxelManager.createRLEVoxelManager({
779
+ image.voxelManager = VoxelManager.createRLEVolumeVoxelManager({
683
780
  dimensions: [width, height, 1],
684
781
  });
685
782
  image.getPixelData = image.voxelManager.getPixelData;
@@ -0,0 +1,2 @@
1
+ import type { Point3 } from '../types';
2
+ export declare function createPositionCallback(imageData: any): (ijk: any, destPoint?: any) => Point3;
@@ -0,0 +1,42 @@
1
+ import * as vec3 from 'gl-matrix/vec3';
2
+ import PointsManager from './PointsManager';
3
+ export function createPositionCallback(imageData) {
4
+ const currentPos = vec3.create();
5
+ const dimensions = imageData.getDimensions();
6
+ const positionI = PointsManager.create3(dimensions[0]);
7
+ const positionJ = PointsManager.create3(dimensions[1]);
8
+ const positionK = PointsManager.create3(dimensions[2]);
9
+ const direction = imageData.getDirection();
10
+ const rowCosines = direction.slice(0, 3);
11
+ const columnCosines = direction.slice(3, 6);
12
+ const scanAxisNormal = direction.slice(6, 9);
13
+ const spacing = imageData.getSpacing();
14
+ const [rowSpacing, columnSpacing, scanAxisSpacing] = spacing;
15
+ const worldPosStart = imageData.indexToWorld([0, 0, 0]);
16
+ const rowStep = vec3.fromValues(rowCosines[0] * rowSpacing, rowCosines[1] * rowSpacing, rowCosines[2] * rowSpacing);
17
+ const columnStep = vec3.fromValues(columnCosines[0] * columnSpacing, columnCosines[1] * columnSpacing, columnCosines[2] * columnSpacing);
18
+ const scanAxisStep = vec3.fromValues(scanAxisNormal[0] * scanAxisSpacing, scanAxisNormal[1] * scanAxisSpacing, scanAxisNormal[2] * scanAxisSpacing);
19
+ const scaled = vec3.create();
20
+ for (let i = 0; i < dimensions[0]; i++) {
21
+ positionI.push(vec3.add(scaled, worldPosStart, vec3.scale(scaled, rowStep, i)));
22
+ }
23
+ for (let j = 0; j < dimensions[0]; j++) {
24
+ positionJ.push(vec3.scale(scaled, columnStep, j));
25
+ }
26
+ for (let k = 0; k < dimensions[0]; k++) {
27
+ positionK.push(vec3.scale(scaled, scanAxisStep, k));
28
+ }
29
+ const dataI = positionI.getTypedArray();
30
+ const dataJ = positionJ.getTypedArray();
31
+ const dataK = positionK.getTypedArray();
32
+ return (ijk, destPoint = currentPos) => {
33
+ const [i, j, k] = ijk;
34
+ const offsetI = i * 3;
35
+ const offsetJ = j * 3;
36
+ const offsetK = k * 3;
37
+ destPoint[0] = dataI[offsetI] + dataJ[offsetJ] + dataK[offsetK];
38
+ destPoint[1] = dataI[offsetI + 1] + dataJ[offsetJ + 1] + dataK[offsetK + 1];
39
+ destPoint[2] = dataI[offsetI + 2] + dataJ[offsetJ + 2] + dataK[offsetK + 2];
40
+ return destPoint;
41
+ };
42
+ }
@@ -0,0 +1,21 @@
1
+ export type Memo = {
2
+ restoreMemo: (undo?: boolean) => void;
3
+ };
4
+ export type Memoable = {
5
+ createMemo: () => Memo;
6
+ };
7
+ export declare class HistoryMemo {
8
+ readonly label: any;
9
+ private _size;
10
+ private position;
11
+ private redoAvailable;
12
+ private undoAvailable;
13
+ private ring;
14
+ constructor(label?: string, size?: number);
15
+ get size(): any;
16
+ undo(items?: number): void;
17
+ redo(items?: number): void;
18
+ push(item: Memo | Memoable): Memo;
19
+ }
20
+ declare const DefaultHistoryMemo: HistoryMemo;
21
+ export { DefaultHistoryMemo };
@@ -0,0 +1,54 @@
1
+ export class HistoryMemo {
2
+ constructor(label = 'Tools', size = 50) {
3
+ this.position = -1;
4
+ this.redoAvailable = 0;
5
+ this.undoAvailable = 0;
6
+ this.ring = new Array();
7
+ this.label = label;
8
+ this._size = size;
9
+ }
10
+ get size() {
11
+ return this._size;
12
+ }
13
+ undo(items = 1) {
14
+ while (items > 0 && this.undoAvailable > 0) {
15
+ const item = this.ring[this.position];
16
+ item.restoreMemo(true);
17
+ items--;
18
+ this.redoAvailable++;
19
+ this.undoAvailable--;
20
+ this.position = (this.position - 1 + this.size) % this.size;
21
+ }
22
+ }
23
+ redo(items = 1) {
24
+ while (items > 0 && this.redoAvailable > 0) {
25
+ const newPosition = (this.position + 1) % this.size;
26
+ const item = this.ring[newPosition];
27
+ item.restoreMemo(false);
28
+ items--;
29
+ this.position = newPosition;
30
+ this.undoAvailable++;
31
+ this.redoAvailable--;
32
+ }
33
+ }
34
+ push(item) {
35
+ if (!item) {
36
+ return;
37
+ }
38
+ const memo = item.restoreMemo
39
+ ? item
40
+ : item.createMemo?.();
41
+ if (!memo) {
42
+ return;
43
+ }
44
+ this.redoAvailable = 0;
45
+ if (this.undoAvailable < this._size) {
46
+ this.undoAvailable++;
47
+ }
48
+ this.position = (this.position + 1) % this._size;
49
+ this.ring[this.position] = memo;
50
+ return memo;
51
+ }
52
+ }
53
+ const DefaultHistoryMemo = new HistoryMemo();
54
+ export { DefaultHistoryMemo };
@@ -23,6 +23,7 @@ import getViewportsWithVolumeId from './getViewportsWithVolumeId';
23
23
  import transformWorldToIndex from './transformWorldToIndex';
24
24
  import transformIndexToWorld from './transformIndexToWorld';
25
25
  import loadImageToCanvas from './loadImageToCanvas';
26
+ import * as HistoryMemo from './historyMemo';
26
27
  import renderToCanvasCPU from './renderToCanvasCPU';
27
28
  import renderToCanvasGPU from './renderToCanvasGPU';
28
29
  import worldToImageCoords from './worldToImageCoords';
@@ -84,4 +85,4 @@ import { jumpToSlice } from './jumpToSlice';
84
85
  import scroll from './scroll';
85
86
  import clip from './clip';
86
87
  declare const getViewportModality: (viewport: IViewport, volumeId?: string) => string;
87
- export { eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, planar, getMinMax, getRuntimeId, isEqual, isEqualAbs, isEqualNegative, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, };
88
+ export { eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, planar, getMinMax, getRuntimeId, isEqual, isEqualAbs, isEqualNegative, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, HistoryMemo, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, };
@@ -23,6 +23,7 @@ import getViewportsWithVolumeId from './getViewportsWithVolumeId';
23
23
  import transformWorldToIndex from './transformWorldToIndex';
24
24
  import transformIndexToWorld from './transformIndexToWorld';
25
25
  import loadImageToCanvas from './loadImageToCanvas';
26
+ import * as HistoryMemo from './historyMemo';
26
27
  import renderToCanvasCPU from './renderToCanvasCPU';
27
28
  import renderToCanvasGPU from './renderToCanvasGPU';
28
29
  import worldToImageCoords from './worldToImageCoords';
@@ -85,4 +86,4 @@ import { jumpToSlice } from './jumpToSlice';
85
86
  import scroll from './scroll';
86
87
  import clip from './clip';
87
88
  const getViewportModality = (viewport, volumeId) => _getViewportModality(viewport, volumeId, cache.getVolume);
88
- export { eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, planar, getMinMax, getRuntimeId, isEqual, isEqualAbs, isEqualNegative, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, };
89
+ export { eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, planar, getMinMax, getRuntimeId, isEqual, isEqualAbs, isEqualNegative, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, HistoryMemo, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, };