@cornerstonejs/core 1.36.3 → 1.37.1

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 (45) hide show
  1. package/dist/cjs/RenderingEngine/StackViewport.js +3 -0
  2. package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
  3. package/dist/cjs/RenderingEngine/Viewport.d.ts +1 -0
  4. package/dist/cjs/RenderingEngine/Viewport.js +14 -8
  5. package/dist/cjs/RenderingEngine/Viewport.js.map +1 -1
  6. package/dist/cjs/types/BoundsIJK.d.ts +3 -0
  7. package/dist/cjs/types/BoundsIJK.js +3 -0
  8. package/dist/cjs/types/BoundsIJK.js.map +1 -0
  9. package/dist/cjs/types/index.d.ts +2 -1
  10. package/dist/cjs/utilities/VoxelManager.d.ts +36 -0
  11. package/dist/cjs/utilities/VoxelManager.js +161 -0
  12. package/dist/cjs/utilities/VoxelManager.js.map +1 -0
  13. package/dist/cjs/utilities/index.d.ts +2 -1
  14. package/dist/cjs/utilities/index.js +3 -1
  15. package/dist/cjs/utilities/index.js.map +1 -1
  16. package/dist/esm/RenderingEngine/StackViewport.js +3 -0
  17. package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
  18. package/dist/esm/RenderingEngine/Viewport.js +14 -8
  19. package/dist/esm/RenderingEngine/Viewport.js.map +1 -1
  20. package/dist/esm/types/BoundsIJK.js +2 -0
  21. package/dist/esm/types/BoundsIJK.js.map +1 -0
  22. package/dist/esm/utilities/VoxelManager.js +157 -0
  23. package/dist/esm/utilities/VoxelManager.js.map +1 -0
  24. package/dist/esm/utilities/index.js +2 -1
  25. package/dist/esm/utilities/index.js.map +1 -1
  26. package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
  27. package/dist/types/RenderingEngine/Viewport.d.ts +1 -0
  28. package/dist/types/RenderingEngine/Viewport.d.ts.map +1 -1
  29. package/dist/types/types/BoundsIJK.d.ts +4 -0
  30. package/dist/types/types/BoundsIJK.d.ts.map +1 -0
  31. package/dist/types/types/index.d.ts +2 -1
  32. package/dist/types/types/index.d.ts.map +1 -1
  33. package/dist/types/utilities/VoxelManager.d.ts +37 -0
  34. package/dist/types/utilities/VoxelManager.d.ts.map +1 -0
  35. package/dist/types/utilities/index.d.ts +2 -1
  36. package/dist/types/utilities/index.d.ts.map +1 -1
  37. package/dist/umd/index.js +1 -1
  38. package/dist/umd/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/RenderingEngine/StackViewport.ts +3 -0
  41. package/src/RenderingEngine/Viewport.ts +19 -9
  42. package/src/types/BoundsIJK.ts +5 -0
  43. package/src/types/index.ts +2 -0
  44. package/src/utilities/VoxelManager.ts +296 -0
  45. package/src/utilities/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "1.36.3",
3
+ "version": "1.37.1",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/index.d.ts",
@@ -47,5 +47,5 @@
47
47
  "type": "individual",
48
48
  "url": "https://ohif.org/donate"
49
49
  },
50
- "gitHead": "b6b6f9273ca4cff849336005d199c4b1b1570815"
50
+ "gitHead": "81e281d066bc2d92c2b7eb7cebbaae49da439fa8"
51
51
  }
@@ -1193,6 +1193,8 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader {
1193
1193
  }
1194
1194
 
1195
1195
  private setRotationGPU(rotation: number): void {
1196
+ const pan = this.getPan();
1197
+ this.setPan([0, 0]);
1196
1198
  const { flipVertical } = this.getCamera();
1197
1199
 
1198
1200
  // Moving back to zero rotation, for new scrolled slice rotation is 0 after camera reset
@@ -1206,6 +1208,7 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader {
1206
1208
 
1207
1209
  // rotating camera to the new value
1208
1210
  this.getVtkActiveCamera().roll(-rotation);
1211
+ this.setPan(pan);
1209
1212
  }
1210
1213
 
1211
1214
  private setInterpolationTypeGPU(interpolationType: InterpolationType): void {
@@ -55,6 +55,11 @@ class Viewport implements IViewport {
55
55
  readonly renderingEngineId: string;
56
56
  /** Type of viewport */
57
57
  readonly type: ViewportType;
58
+ /**
59
+ * The amount by which the images are inset in a viewport by default.
60
+ */
61
+ protected insetImageMultiplier = 1.1;
62
+
58
63
  protected flipHorizontal = false;
59
64
  protected flipVertical = false;
60
65
  public isDisabled: boolean;
@@ -584,14 +589,15 @@ class Viewport implements IViewport {
584
589
  const { storeAsInitialCamera } = displayArea;
585
590
 
586
591
  // make calculations relative to the fitToCanvasCamera view
587
- this.setCamera(this.fitToCanvasCamera, false);
592
+ this.setCamera(this.fitToCanvasCamera, storeAsInitialCamera);
588
593
 
589
594
  const { imageArea, imageCanvasPoint } = displayArea;
590
595
 
596
+ let zoom = 1;
591
597
  if (imageArea) {
592
598
  const [areaX, areaY] = imageArea;
593
- const zoom = Math.min(this.getZoom() / areaX, this.getZoom() / areaY);
594
- this.setZoom(zoom, storeAsInitialCamera);
599
+ zoom = Math.min(this.getZoom() / areaX, this.getZoom() / areaY);
600
+ this.setZoom(this.insetImageMultiplier * zoom, storeAsInitialCamera);
595
601
  }
596
602
 
597
603
  // getting the image info
@@ -604,18 +610,22 @@ class Viewport implements IViewport {
604
610
  const validateCanvasPanY = this.sHeight / devicePixelRatio;
605
611
  const canvasPanX = validateCanvasPanX * (canvasX - 0.5);
606
612
  const canvasPanY = validateCanvasPanY * (canvasY - 0.5);
607
-
608
613
  const dimensions = imageData.getDimensions();
609
614
  const canvasZero = this.worldToCanvas([0, 0, 0]);
610
- const canvasEdge = this.worldToCanvas(dimensions);
615
+ const canvasEdge = this.worldToCanvas([
616
+ dimensions[0] - 1,
617
+ dimensions[1] - 1,
618
+ dimensions[2],
619
+ ]);
611
620
  const canvasImage = [
612
621
  canvasEdge[0] - canvasZero[0],
613
622
  canvasEdge[1] - canvasZero[1],
614
623
  ];
615
624
  const [imgWidth, imgHeight] = canvasImage;
616
625
  const [imageX, imageY] = imagePoint;
617
- const imagePanX = imgWidth * (0.5 - imageX);
618
- const imagePanY = imgHeight * (0.5 - imageY);
626
+ const imagePanX =
627
+ (zoom * imgWidth * (0.5 - imageX) * validateCanvasPanY) / imgHeight;
628
+ const imagePanY = zoom * validateCanvasPanY * (0.5 - imageY);
619
629
 
620
630
  const newPositionX = imagePanX + canvasPanX;
621
631
  const newPositionY = imagePanY + canvasPanY;
@@ -727,7 +737,7 @@ class Viewport implements IViewport {
727
737
  }
728
738
 
729
739
  //const angle = vtkMath.radiansFromDegrees(activeCamera.getViewAngle())
730
- const parallelScale = 1.1 * radius;
740
+ const parallelScale = this.insetImageMultiplier * radius;
731
741
 
732
742
  let w1 = bounds[1] - bounds[0];
733
743
  let w2 = bounds[3] - bounds[2];
@@ -743,7 +753,7 @@ class Viewport implements IViewport {
743
753
  // compute the radius of the enclosing sphere
744
754
  radius = Math.sqrt(radius) * 0.5;
745
755
 
746
- const distance = 1.1 * radius;
756
+ const distance = this.insetImageMultiplier * radius;
747
757
 
748
758
  const viewUpToSet: Point3 =
749
759
  Math.abs(vtkMath.dot(viewUp, viewPlaneNormal)) > 0.999
@@ -0,0 +1,5 @@
1
+ import type Point2 from './Point2';
2
+
3
+ type BoundsIJK = [Point2, Point2, Point2];
4
+
5
+ export default BoundsIJK;
@@ -105,6 +105,7 @@ import type {
105
105
  InternalVideoCamera,
106
106
  VideoViewportInput,
107
107
  } from './VideoViewportTypes';
108
+ import type BoundsIJK from './BoundsIJK';
108
109
 
109
110
  export type {
110
111
  // config
@@ -212,6 +213,7 @@ export type {
212
213
  // video
213
214
  InternalVideoCamera,
214
215
  VideoViewportInput,
216
+ BoundsIJK,
215
217
  Color,
216
218
  ColorLUT,
217
219
  };
@@ -0,0 +1,296 @@
1
+ import type { BoundsIJK, Point3, VolumeScalarData } from '../types';
2
+
3
+ /**
4
+ * This is a simple, standard interface to values associated with a voxel.
5
+ */
6
+ export default class VoxelManager<T> {
7
+ public modifiedSlices = new Set<number>();
8
+ public boundsIJK = [
9
+ [Infinity, -Infinity],
10
+ [Infinity, -Infinity],
11
+ [Infinity, -Infinity],
12
+ ] as BoundsIJK;
13
+
14
+ // Provide direct access to the underlying data, if any
15
+ public scalarData: VolumeScalarData;
16
+ public map: Map<number, T>;
17
+ public sourceVoxelManager: VoxelManager<T>;
18
+ public isInObject: (pointIPS, pointIJK) => boolean;
19
+ public readonly dimensions: Point3;
20
+
21
+ points: Set<number>;
22
+ width: number;
23
+ frameSize: number;
24
+ _get: (index: number) => T;
25
+ _set: (index: number, v: T) => boolean | void;
26
+
27
+ /**
28
+ * Creates a generic voxel value accessor, with access to the values
29
+ * provided by the _get and optionally _set values.
30
+ * @param dimensions - for the voxel volume
31
+ * @param _get - called to get a value by index
32
+ * @param _set - called when setting a value
33
+ */
34
+ constructor(
35
+ dimensions,
36
+ _get: (index: number) => T,
37
+ _set?: (index: number, v: T) => boolean | void
38
+ ) {
39
+ this.dimensions = dimensions;
40
+ this.width = dimensions[0];
41
+ this.frameSize = this.width * dimensions[1];
42
+ this._get = _get;
43
+ this._set = _set;
44
+ }
45
+
46
+ /**
47
+ * Gets the voxel value at position i,j,k.
48
+ */
49
+ public getAtIJK = (i, j, k) => {
50
+ const index = i + j * this.width + k * this.frameSize;
51
+ return this._get(index);
52
+ };
53
+
54
+ /**
55
+ * Sets the voxel value at position i,j,k and records the slice
56
+ * that was modified.
57
+ */
58
+ public setAtIJK = (i: number, j: number, k: number, v) => {
59
+ const index = i + j * this.width + k * this.frameSize;
60
+ if (this._set(index, v) !== false) {
61
+ this.modifiedSlices.add(k);
62
+ VoxelManager.addBounds(this.boundsIJK, [i, j, k]);
63
+ }
64
+ };
65
+
66
+ /**
67
+ * Adds a point as an array or an index value to the set of points
68
+ * associated with this voxel value.
69
+ * Can be used for tracking clicked points or other modified values.
70
+ */
71
+ public addPoint(point: Point3 | number) {
72
+ const index = Array.isArray(point)
73
+ ? point[0] + this.width * point[1] + this.frameSize * point[2]
74
+ : point;
75
+ if (!this.points) {
76
+ this.points = new Set<number>();
77
+ }
78
+ this.points.add(index);
79
+ }
80
+
81
+ /**
82
+ * Gets the list of added points as an array of Point3 values
83
+ */
84
+ public getPoints(): Point3[] {
85
+ return this.points
86
+ ? [...this.points].map((index) => this.toIJK(index))
87
+ : [];
88
+ }
89
+
90
+ /**
91
+ * Gets the points added using addPoint as an array of indices.
92
+ */
93
+ public getPointIndices(): number[] {
94
+ return this.points ? [...this.points] : [];
95
+ }
96
+
97
+ /**
98
+ * Gets the voxel value at the given Point3 location.
99
+ */
100
+ public getAtIJKPoint = ([i, j, k]) => this.getAtIJK(i, j, k);
101
+
102
+ /**
103
+ * Sets the voxel value at the given point3 location to the specified value.
104
+ * Records the z index modified.
105
+ * Will record the index value if the VoxelManager is backed by a map.
106
+ */
107
+ public setAtIJKPoint = ([i, j, k], v) => this.setAtIJK(i, j, k, v);
108
+
109
+ /**
110
+ * Gets the value at the given index.
111
+ */
112
+ public getAtIndex = (index) => this._get(index);
113
+
114
+ /**
115
+ * Sets the value at the given index
116
+ */
117
+ public setAtIndex = (index, v) => {
118
+ if (this._set(index, v) !== false) {
119
+ const pointIJK = this.toIJK(index);
120
+ this.modifiedSlices.add(pointIJK[2]);
121
+ VoxelManager.addBounds(this.boundsIJK, pointIJK);
122
+ }
123
+ };
124
+
125
+ /**
126
+ * Converts an index value to a Point3 IJK value
127
+ */
128
+ public toIJK(index: number): Point3 {
129
+ return [
130
+ index % this.width,
131
+ Math.floor((index % this.frameSize) / this.width),
132
+ Math.floor(index / this.frameSize),
133
+ ];
134
+ }
135
+
136
+ /**
137
+ * Converts an IJK Point3 value to an index value
138
+ */
139
+ public toIndex(ijk: Point3) {
140
+ return ijk[0] + ijk[1] * this.width + ijk[2] * this.frameSize;
141
+ }
142
+
143
+ /**
144
+ * Gets the bounds for the modified set of values.
145
+ */
146
+ public getBoundsIJK(): BoundsIJK {
147
+ if (this.boundsIJK[0][0] < this.dimensions[0]) {
148
+ return this.boundsIJK;
149
+ }
150
+ return this.dimensions.map((dimension) => [0, dimension - 1]) as BoundsIJK;
151
+ }
152
+
153
+ /**
154
+ * Iterate over the points within the bounds, or the modified points if recorded.
155
+ */
156
+ public forEach = (callback, options?) => {
157
+ const boundsIJK = options?.boundsIJK || this.getBoundsIJK();
158
+ const { isWithinObject } = options || {};
159
+ if (this.map) {
160
+ // Optimize this for only values in the map
161
+ for (const index of this.map.keys()) {
162
+ const pointIJK = this.toIJK(index);
163
+ const value = this._get(index);
164
+ const callbackArguments = { value, index, pointIJK };
165
+ if (isWithinObject?.(callbackArguments) === false) {
166
+ continue;
167
+ }
168
+ callback(callbackArguments);
169
+ }
170
+ } else {
171
+ for (let k = boundsIJK[2][0]; k <= boundsIJK[2][1]; k++) {
172
+ const kIndex = k * this.frameSize;
173
+ for (let j = boundsIJK[1][0]; j <= boundsIJK[1][1]; j++) {
174
+ const jIndex = kIndex + j * this.width;
175
+ for (
176
+ let i = boundsIJK[0][0], index = jIndex + i;
177
+ i <= boundsIJK[0][1];
178
+ i++, index++
179
+ ) {
180
+ const value = this.getAtIndex(index);
181
+ const callbackArguments = { value, index, pointIJK: [i, j, k] };
182
+ if (isWithinObject?.(callbackArguments) === false) {
183
+ continue;
184
+ }
185
+ callback(callbackArguments);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ };
191
+
192
+ /**
193
+ * Clears any map specific data, as wellas the modified slices, points and
194
+ * bounds.
195
+ */
196
+ public clear() {
197
+ if (this.map) {
198
+ this.map.clear();
199
+ }
200
+ this.boundsIJK.map((bound) => {
201
+ bound[0] = Infinity;
202
+ bound[1] = -Infinity;
203
+ });
204
+ this.modifiedSlices.clear();
205
+ this.points?.clear();
206
+ }
207
+
208
+ /**
209
+ * @returns The array of modified k indices
210
+ */
211
+ public getArrayOfSlices(): number[] {
212
+ return Array.from(this.modifiedSlices);
213
+ }
214
+
215
+ /**
216
+ * Extends the bounds of this object to include the specified point
217
+ */
218
+ public static addBounds(bounds: BoundsIJK, point: Point3) {
219
+ bounds.forEach((bound, index) => {
220
+ bound[0] = Math.min(point[index], bound[0]);
221
+ bound[1] = Math.max(point[index], bound[1]);
222
+ });
223
+ }
224
+
225
+ /**
226
+ * Creates a volume value accessor, based on a volume scalar data instance.
227
+ * This also works for image value accessors for single plane (k=0) accessors.
228
+ */
229
+ public static createVolumeVoxelManager(
230
+ dimensions: Point3,
231
+ scalarData
232
+ ): VoxelManager<number> {
233
+ const voxels = new VoxelManager(
234
+ dimensions,
235
+ (index) => scalarData[index],
236
+ (index, v) => {
237
+ const isChanged = scalarData[index] !== v;
238
+ scalarData[index] = v;
239
+ return isChanged;
240
+ }
241
+ );
242
+ voxels.scalarData = scalarData;
243
+ return voxels;
244
+ }
245
+
246
+ /**
247
+ * Creates a volume map value accessor. This is initially empty and
248
+ * the map stores the index to value instances.
249
+ * This is useful for sparse matrices containing pixel data.
250
+ */
251
+ public static createMapVoxelManager<T>(dimension: Point3): VoxelManager<T> {
252
+ const map = new Map<number, T>();
253
+ const voxelManager = new VoxelManager(
254
+ dimension,
255
+ map.get.bind(map),
256
+ (index, v) => map.set(index, v) && true
257
+ );
258
+ voxelManager.map = map;
259
+ return voxelManager;
260
+ }
261
+
262
+ /**
263
+ * Creates a history remembering voxel manager.
264
+ * This will remember the original values in the voxels, and will apply the
265
+ * update to the underlying source voxel manager.
266
+ */
267
+ public static createHistoryVoxelManager<T>(
268
+ sourceVoxelManager: VoxelManager<T>
269
+ ): VoxelManager<T> {
270
+ const map = new Map<number, T>();
271
+ const { dimensions } = sourceVoxelManager;
272
+ const voxelManager = new VoxelManager(
273
+ dimensions,
274
+ (index) => map.get(index),
275
+ function (index, v) {
276
+ if (!map.has(index)) {
277
+ const oldV = this.sourceVoxelManager.getAtIndex(index);
278
+ if (oldV === v) {
279
+ // No-op
280
+ return false;
281
+ }
282
+ map.set(index, oldV);
283
+ } else if (v === map.get(index)) {
284
+ map.delete(index);
285
+ }
286
+ this.sourceVoxelManager.setAtIndex(index, v);
287
+ }
288
+ );
289
+ voxelManager.map = map;
290
+ voxelManager.scalarData = sourceVoxelManager.scalarData;
291
+ voxelManager.sourceVoxelManager = sourceVoxelManager;
292
+ return voxelManager;
293
+ }
294
+ }
295
+
296
+ export type { VoxelManager };
@@ -59,6 +59,7 @@ import decimate from './decimate';
59
59
  import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
60
60
  import isVideoTransferSyntax from './isVideoTransferSyntax';
61
61
  import { getBufferConfiguration } from './getBufferConfiguration';
62
+ import VoxelManager from './VoxelManager';
62
63
 
63
64
  // name spaces
64
65
  import * as planar from './planar';
@@ -133,4 +134,5 @@ export {
133
134
  genericMetadataProvider,
134
135
  isVideoTransferSyntax,
135
136
  getBufferConfiguration,
137
+ VoxelManager,
136
138
  };