@cornerstonejs/core 1.36.3 → 1.37.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.
- package/dist/cjs/types/BoundsIJK.d.ts +3 -0
- package/dist/cjs/types/BoundsIJK.js +3 -0
- package/dist/cjs/types/BoundsIJK.js.map +1 -0
- package/dist/cjs/types/index.d.ts +2 -1
- package/dist/cjs/utilities/VoxelManager.d.ts +36 -0
- package/dist/cjs/utilities/VoxelManager.js +161 -0
- package/dist/cjs/utilities/VoxelManager.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +2 -1
- package/dist/cjs/utilities/index.js +3 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/esm/types/BoundsIJK.js +2 -0
- package/dist/esm/types/BoundsIJK.js.map +1 -0
- package/dist/esm/utilities/VoxelManager.js +157 -0
- package/dist/esm/utilities/VoxelManager.js.map +1 -0
- package/dist/esm/utilities/index.js +2 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/types/types/BoundsIJK.d.ts +4 -0
- package/dist/types/types/BoundsIJK.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +2 -1
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utilities/VoxelManager.d.ts +37 -0
- package/dist/types/utilities/VoxelManager.d.ts.map +1 -0
- package/dist/types/utilities/index.d.ts +2 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
- package/src/types/BoundsIJK.ts +5 -0
- package/src/types/index.ts +2 -0
- package/src/utilities/VoxelManager.ts +296 -0
- 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.
|
|
3
|
+
"version": "1.37.0",
|
|
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": "
|
|
50
|
+
"gitHead": "5816f19627715147dc453e33e1480bf66dd63cf6"
|
|
51
51
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -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 };
|
package/src/utilities/index.ts
CHANGED
|
@@ -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
|
};
|