@cornerstonejs/core 2.3.2 → 2.4.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/esm/RenderingEngine/helpers/setDefaultVolumeVOI.js +5 -3
- package/dist/esm/enums/VoxelManagerEnum.d.ts +5 -0
- package/dist/esm/enums/VoxelManagerEnum.js +6 -0
- package/dist/esm/enums/index.d.ts +2 -1
- package/dist/esm/enums/index.js +2 -1
- package/dist/esm/loaders/imageLoader.d.ts +3 -0
- package/dist/esm/loaders/imageLoader.js +13 -9
- package/dist/esm/loaders/volumeLoader.d.ts +2 -0
- package/dist/esm/loaders/volumeLoader.js +6 -1
- package/dist/esm/types/PixelDataTypedArray.d.ts +1 -1
- package/dist/esm/utilities/PointsManager.d.ts +1 -0
- package/dist/esm/utilities/PointsManager.js +3 -0
- package/dist/esm/utilities/RLEVoxelMap.d.ts +42 -6
- package/dist/esm/utilities/RLEVoxelMap.js +213 -12
- package/dist/esm/utilities/VoxelManager.d.ts +30 -10
- package/dist/esm/utilities/VoxelManager.js +226 -129
- package/dist/esm/utilities/createPositionCallback.d.ts +2 -0
- package/dist/esm/utilities/createPositionCallback.js +42 -0
- package/dist/esm/utilities/pointInShapeCallback.d.ts +15 -1
- package/dist/esm/utilities/pointInShapeCallback.js +62 -51
- package/package.json +2 -2
|
@@ -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,
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
332
|
-
index
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
361
|
-
const pixelIndex =
|
|
362
|
-
return {
|
|
405
|
+
const voxelManager = image.voxelManager;
|
|
406
|
+
const pixelIndex = index % pixelsPerSlice;
|
|
407
|
+
return { voxelManager, pixelIndex };
|
|
363
408
|
}
|
|
364
409
|
function getVoxelValue(index) {
|
|
365
|
-
const {
|
|
366
|
-
if (!
|
|
410
|
+
const { voxelManager: imageVoxelManager, pixelIndex } = getPixelInfo(index);
|
|
411
|
+
if (!imageVoxelManager || pixelIndex === null) {
|
|
367
412
|
return null;
|
|
368
413
|
}
|
|
369
|
-
|
|
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 {
|
|
382
|
-
if (!
|
|
417
|
+
const { voxelManager: imageVoxelManager, pixelIndex } = getPixelInfo(index);
|
|
418
|
+
if (!imageVoxelManager || pixelIndex === null) {
|
|
383
419
|
return false;
|
|
384
420
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
426
|
+
imageVoxelManager.setAtIndex(pixelIndex, v);
|
|
427
|
+
return true;
|
|
402
428
|
}
|
|
403
|
-
const
|
|
404
|
-
voxelManager.numberOfComponents = numberOfComponents;
|
|
405
|
-
voxelManager._getConstructor = () => {
|
|
429
|
+
const _getConstructor = () => {
|
|
406
430
|
const pixelInfo = getPixelInfo(0);
|
|
407
|
-
if (!pixelInfo
|
|
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,
|
|
547
|
-
|
|
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,
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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,
|
|
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,
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (
|
|
630
|
-
|
|
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.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
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,
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
layer =
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
|
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,
|
|
665
|
-
map.
|
|
666
|
-
|
|
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.
|
|
779
|
+
image.voxelManager = VoxelManager.createRLEVolumeVoxelManager({
|
|
683
780
|
dimensions: [width, height, 1],
|
|
684
781
|
});
|
|
685
782
|
image.getPixelData = image.voxelManager.getPixelData;
|
|
@@ -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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { vec3 } from 'gl-matrix';
|
|
1
|
+
import type { vec3 } from 'gl-matrix';
|
|
2
2
|
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
3
3
|
import type BoundsIJK from '../types/BoundsIJK';
|
|
4
4
|
import type { CPUImageData, Point3 } from '../types';
|
|
@@ -22,3 +22,17 @@ export interface PointInShapeOptions {
|
|
|
22
22
|
returnPoints?: boolean;
|
|
23
23
|
}
|
|
24
24
|
export declare function pointInShapeCallback(imageData: vtkImageData | CPUImageData, options: PointInShapeOptions): Array<PointInShape> | undefined;
|
|
25
|
+
export declare function iterateOverPointsInShape({ imageData, bounds, scalarData, pointInShapeFn, callback, }: {
|
|
26
|
+
imageData: any;
|
|
27
|
+
bounds: any;
|
|
28
|
+
scalarData: any;
|
|
29
|
+
pointInShapeFn: any;
|
|
30
|
+
callback: any;
|
|
31
|
+
}): PointInShape[];
|
|
32
|
+
export declare function iterateOverPointsInShapeVoxelManager({ voxelManager, bounds, imageData, pointInShapeFn, callback, }: {
|
|
33
|
+
voxelManager: any;
|
|
34
|
+
bounds: any;
|
|
35
|
+
imageData: any;
|
|
36
|
+
pointInShapeFn: any;
|
|
37
|
+
callback: any;
|
|
38
|
+
}): PointInShape[];
|