@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.
- 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/historyMemo/index.d.ts +21 -0
- package/dist/esm/utilities/historyMemo/index.js +54 -0
- package/dist/esm/utilities/index.d.ts +2 -1
- package/dist/esm/utilities/index.js +2 -1
- package/dist/esm/utilities/pointInShapeCallback.d.ts +15 -1
- package/dist/esm/utilities/pointInShapeCallback.js +62 -51
- package/package.json +2 -2
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { loadAndCacheImage } from '../../loaders/imageLoader';
|
|
2
2
|
import * as metaData from '../../metaData';
|
|
3
|
-
import getMinMax from '../../utilities/getMinMax';
|
|
4
3
|
import * as windowLevel from '../../utilities/windowLevel';
|
|
5
4
|
import { RequestType } from '../../enums';
|
|
6
5
|
import cache from '../../cache/cache';
|
|
@@ -95,8 +94,11 @@ async function getVOIFromMiddleSliceMinMax(imageVolume) {
|
|
|
95
94
|
if (!imageVolume.referencedImageIds?.length) {
|
|
96
95
|
image = await loadAndCacheImage(imageId, { ...options, ignoreCache: true });
|
|
97
96
|
}
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
let { min, max } = image.voxelManager.getMinMax();
|
|
98
|
+
if (min.length > 1) {
|
|
99
|
+
min = Math.min(...min);
|
|
100
|
+
max = Math.max(...max);
|
|
101
|
+
}
|
|
100
102
|
return {
|
|
101
103
|
lower: min,
|
|
102
104
|
upper: max,
|
|
@@ -14,4 +14,5 @@ import ImageQualityStatus from './ImageQualityStatus';
|
|
|
14
14
|
import * as VideoEnums from './VideoEnums';
|
|
15
15
|
import MetadataModules from './MetadataModules';
|
|
16
16
|
import { GenerateImageType } from './GenerateImageType';
|
|
17
|
-
|
|
17
|
+
import VoxelManagerEnum from './VoxelManagerEnum';
|
|
18
|
+
export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, OrientationAxis, GeometryType, ContourType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, };
|
package/dist/esm/enums/index.js
CHANGED
|
@@ -14,4 +14,5 @@ import ImageQualityStatus from './ImageQualityStatus';
|
|
|
14
14
|
import * as VideoEnums from './VideoEnums';
|
|
15
15
|
import MetadataModules from './MetadataModules';
|
|
16
16
|
import { GenerateImageType } from './GenerateImageType';
|
|
17
|
-
|
|
17
|
+
import VoxelManagerEnum from './VoxelManagerEnum';
|
|
18
|
+
export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, OrientationAxis, GeometryType, ContourType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IImage, ImageLoaderFn, Point2, Point3, Mat3, PixelDataTypedArrayString, PixelDataTypedArray } from '../types';
|
|
2
|
+
import VoxelManagerEnum from '../enums/VoxelManagerEnum';
|
|
2
3
|
export interface ImageLoaderOptions {
|
|
3
4
|
priority: number;
|
|
4
5
|
requestType: string;
|
|
@@ -11,6 +12,7 @@ interface LocalImageOptions {
|
|
|
11
12
|
targetBuffer?: {
|
|
12
13
|
type: PixelDataTypedArrayString;
|
|
13
14
|
};
|
|
15
|
+
voxelRepresentation?: VoxelManagerEnum;
|
|
14
16
|
dimensions?: Point2;
|
|
15
17
|
spacing?: Point2;
|
|
16
18
|
origin?: Point3;
|
|
@@ -31,6 +33,7 @@ export declare function createAndCacheDerivedImages(referencedImageIds: string[]
|
|
|
31
33
|
targetBuffer?: {
|
|
32
34
|
type: PixelDataTypedArrayString;
|
|
33
35
|
};
|
|
36
|
+
voxelRepresentation?: VoxelManagerEnum;
|
|
34
37
|
}): IImage[];
|
|
35
38
|
export declare function createAndCacheLocalImage(imageId: string, options: LocalImageOptions): IImage;
|
|
36
39
|
export declare function cancelLoadImage(imageId: string): void;
|
|
@@ -8,6 +8,7 @@ import uuidv4 from '../utilities/uuidv4';
|
|
|
8
8
|
import VoxelManager from '../utilities/VoxelManager';
|
|
9
9
|
import imageLoadPoolManager from '../requestPool/imageLoadPoolManager';
|
|
10
10
|
import * as metaData from '../metaData';
|
|
11
|
+
import VoxelManagerEnum from '../enums/VoxelManagerEnum';
|
|
11
12
|
const imageLoaders = {};
|
|
12
13
|
let unknownImageLoader;
|
|
13
14
|
function loadImageFromImageLoader(imageId, options) {
|
|
@@ -85,7 +86,7 @@ export function createAndCacheDerivedImage(referencedImageId, options = {}) {
|
|
|
85
86
|
if (options.imageId === undefined) {
|
|
86
87
|
options.imageId = `derived:${uuidv4()}`;
|
|
87
88
|
}
|
|
88
|
-
const { imageId, skipCreateBuffer, onCacheAdd } = options;
|
|
89
|
+
const { imageId, skipCreateBuffer, onCacheAdd, voxelRepresentation } = options;
|
|
89
90
|
const imagePlaneModule = metaData.get('imagePlaneModule', referencedImageId);
|
|
90
91
|
const length = imagePlaneModule.rows * imagePlaneModule.columns;
|
|
91
92
|
const { TypedArrayConstructor } = getBufferConfiguration(options.targetBuffer?.type, length);
|
|
@@ -126,6 +127,7 @@ export function createAndCacheDerivedImage(referencedImageId, options = {}) {
|
|
|
126
127
|
targetBuffer: {
|
|
127
128
|
type: imageScalarData.constructor.name,
|
|
128
129
|
},
|
|
130
|
+
voxelRepresentation,
|
|
129
131
|
dimensions: [imagePlaneModule.columns, imagePlaneModule.rows],
|
|
130
132
|
spacing: [
|
|
131
133
|
imagePlaneModule.columnPixelSpacing,
|
|
@@ -161,7 +163,7 @@ export function createAndCacheDerivedImages(referencedImageIds, options = {}) {
|
|
|
161
163
|
return images;
|
|
162
164
|
}
|
|
163
165
|
export function createAndCacheLocalImage(imageId, options) {
|
|
164
|
-
const { scalarData, origin, direction, targetBuffer, skipCreateBuffer, onCacheAdd, frameOfReferenceUID, } = options;
|
|
166
|
+
const { scalarData, origin, direction, targetBuffer, skipCreateBuffer, onCacheAdd, frameOfReferenceUID, voxelRepresentation, } = options;
|
|
165
167
|
const dimensions = options.dimensions;
|
|
166
168
|
const spacing = options.spacing;
|
|
167
169
|
if (!dimensions || !spacing) {
|
|
@@ -196,7 +198,7 @@ export function createAndCacheLocalImage(imageId, options) {
|
|
|
196
198
|
scalarDataToUse = scalarData;
|
|
197
199
|
}
|
|
198
200
|
else if (!skipCreateBuffer) {
|
|
199
|
-
const {
|
|
201
|
+
const { TypedArrayConstructor } = getBufferConfiguration(targetBuffer?.type, length);
|
|
200
202
|
const imageScalarData = new TypedArrayConstructor(length);
|
|
201
203
|
scalarDataToUse = imageScalarData;
|
|
202
204
|
}
|
|
@@ -245,12 +247,14 @@ export function createAndCacheLocalImage(imageId, options) {
|
|
|
245
247
|
metadata: metadata[type] || {},
|
|
246
248
|
});
|
|
247
249
|
});
|
|
248
|
-
const voxelManager =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
const voxelManager = (voxelRepresentation === VoxelManagerEnum.RLE &&
|
|
251
|
+
VoxelManager.createRLEImageVoxelManager({ dimensions })) ||
|
|
252
|
+
VoxelManager.createImageVoxelManager({
|
|
253
|
+
height,
|
|
254
|
+
width,
|
|
255
|
+
numberOfComponents,
|
|
256
|
+
scalarData: scalarDataToUse,
|
|
257
|
+
});
|
|
254
258
|
let minPixelValue = scalarDataToUse[0];
|
|
255
259
|
let maxPixelValue = scalarDataToUse[0];
|
|
256
260
|
for (let i = 1; i < scalarDataToUse.length; i++) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import '@kitware/vtk.js/Rendering/Profiles/Volume';
|
|
2
|
+
import type VoxelManagerEnum from '../enums/VoxelManagerEnum';
|
|
2
3
|
import type { Point3, Metadata, Mat3, IImageVolume, VolumeLoaderFn, PixelDataTypedArray, PixelDataTypedArrayString, IStreamingImageVolume } from '../types';
|
|
3
4
|
interface VolumeLoaderOptions {
|
|
4
5
|
imageIds: string[];
|
|
@@ -9,6 +10,7 @@ interface DerivedVolumeOptions {
|
|
|
9
10
|
targetBuffer?: {
|
|
10
11
|
type: PixelDataTypedArrayString;
|
|
11
12
|
};
|
|
13
|
+
voxelRepresentation?: VoxelManagerEnum;
|
|
12
14
|
}
|
|
13
15
|
export interface LocalVolumeOptions {
|
|
14
16
|
metadata: Metadata;
|
|
@@ -65,6 +65,7 @@ export function createAndCacheDerivedVolume(referencedVolumeId, options) {
|
|
|
65
65
|
throw new Error(`Cannot created derived volume: Referenced volume with id ${referencedVolumeId} does not exist.`);
|
|
66
66
|
}
|
|
67
67
|
let { volumeId } = options;
|
|
68
|
+
const { voxelRepresentation } = options;
|
|
68
69
|
if (volumeId === undefined) {
|
|
69
70
|
volumeId = uuidv4();
|
|
70
71
|
}
|
|
@@ -74,6 +75,7 @@ export function createAndCacheDerivedVolume(referencedVolumeId, options) {
|
|
|
74
75
|
: referencedVolume.imageIds ?? [];
|
|
75
76
|
const derivedImages = createAndCacheDerivedImages(referencedImageIds, {
|
|
76
77
|
targetBuffer: options.targetBuffer,
|
|
78
|
+
voxelRepresentation,
|
|
77
79
|
});
|
|
78
80
|
const dataType = derivedImages[0].dataType;
|
|
79
81
|
const derivedVolumeImageIds = derivedImages.map((image) => image.imageId);
|
|
@@ -222,7 +224,10 @@ export function getUnknownVolumeLoaderSchema() {
|
|
|
222
224
|
export function createAndCacheDerivedLabelmapVolume(referencedVolumeId, options = {}) {
|
|
223
225
|
return createAndCacheDerivedVolume(referencedVolumeId, {
|
|
224
226
|
...options,
|
|
225
|
-
targetBuffer: {
|
|
227
|
+
targetBuffer: {
|
|
228
|
+
type: 'Uint8Array',
|
|
229
|
+
...options?.targetBuffer,
|
|
230
|
+
},
|
|
226
231
|
});
|
|
227
232
|
}
|
|
228
233
|
export function createLocalLabelmapVolume(options, volumeId, preventCache = false) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type PixelDataTypedArray = Float32Array | Int16Array | Uint16Array | Uint8Array | Int8Array | Uint8ClampedArray;
|
|
2
|
-
export type PixelDataTypedArrayString = 'Float32Array' | 'Int16Array' | 'Uint16Array' | 'Uint8Array' | 'Int8Array' | 'Uint8ClampedArray';
|
|
2
|
+
export type PixelDataTypedArrayString = 'Float32Array' | 'Int16Array' | 'Uint16Array' | 'Uint8Array' | 'Int8Array' | 'Uint8ClampedArray' | 'none';
|
|
@@ -20,6 +20,7 @@ export default class PointsManager<T> {
|
|
|
20
20
|
getPointArray(index: number): T;
|
|
21
21
|
protected grow(additionalSize?: number, growSize?: number): void;
|
|
22
22
|
reverse(): void;
|
|
23
|
+
getTypedArray(): Float32Array;
|
|
23
24
|
push(point: T): void;
|
|
24
25
|
map<R>(f: (value: any, index: number) => R): R[];
|
|
25
26
|
get points(): T[];
|
|
@@ -1,21 +1,57 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type Point3 from '../types/Point3';
|
|
2
|
+
import type BoundsIJK from '../types/BoundsIJK';
|
|
3
|
+
import type { PixelDataTypedArray } from '../types';
|
|
4
|
+
export type RLERun<T> = {
|
|
5
|
+
value: T;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
};
|
|
9
|
+
export type PlaneNormalizer = {
|
|
10
|
+
toIJK: (ijkPrime: Point3) => Point3;
|
|
11
|
+
fromIJK: (ijk: Point3) => Point3;
|
|
12
|
+
boundsIJKPrime: BoundsIJK;
|
|
13
|
+
};
|
|
2
14
|
export default class RLEVoxelMap<T> {
|
|
15
|
+
normalizer: PlaneNormalizer;
|
|
3
16
|
protected rows: Map<number, RLERun<T>[]>;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
17
|
+
height: number;
|
|
18
|
+
width: number;
|
|
19
|
+
depth: number;
|
|
7
20
|
protected jMultiple: number;
|
|
8
21
|
protected kMultiple: number;
|
|
9
|
-
protected
|
|
22
|
+
protected numComps: number;
|
|
10
23
|
defaultValue: T;
|
|
11
24
|
pixelDataConstructor: Uint8ArrayConstructor;
|
|
25
|
+
static copyMap<T>(destination: RLEVoxelMap<T>, source: RLEVoxelMap<T>): void;
|
|
12
26
|
constructor(width: number, height: number, depth?: number);
|
|
27
|
+
static getScalarData: (ArrayType?: Uint8ClampedArrayConstructor) => Uint8ClampedArray;
|
|
28
|
+
updateScalarData: (scalarData: PixelDataTypedArray) => void;
|
|
13
29
|
get: (index: number) => T;
|
|
14
|
-
|
|
30
|
+
toIJK(index: number): Point3;
|
|
31
|
+
toIndex([i, j, k]: Point3): number;
|
|
32
|
+
protected getRLE(i: number, j: number, k?: number): RLERun<T>;
|
|
33
|
+
has(index: number): boolean;
|
|
34
|
+
delete(index: number): void;
|
|
15
35
|
protected findIndex(row: RLERun<T>[], i: number): number;
|
|
36
|
+
forEach(callback: any, options?: {
|
|
37
|
+
rowModified?: boolean;
|
|
38
|
+
}): void;
|
|
39
|
+
forEachRow(callback: any): void;
|
|
16
40
|
getRun: (j: number, k: number) => RLERun<T>[];
|
|
17
41
|
set: (index: number, value: T) => void;
|
|
18
42
|
clear(): void;
|
|
19
43
|
keys(): number[];
|
|
20
44
|
getPixelData(k?: number, pixelData?: PixelDataTypedArray): PixelDataTypedArray;
|
|
45
|
+
floodFill(i: number, j: number, k: number, value: T, options?: {
|
|
46
|
+
planar?: boolean;
|
|
47
|
+
diagonals?: boolean;
|
|
48
|
+
singlePlane?: boolean;
|
|
49
|
+
}): number;
|
|
50
|
+
private flood;
|
|
51
|
+
fillFrom(getter: (i: number, j: number, k: number) => T, boundsIJK: BoundsIJK): void;
|
|
52
|
+
findAdjacents(item: [RLERun<T>, number, number, Point3[]?], { diagonals, planar, singlePlane }: {
|
|
53
|
+
diagonals?: boolean;
|
|
54
|
+
planar?: boolean;
|
|
55
|
+
singlePlane?: boolean;
|
|
56
|
+
}): any[];
|
|
21
57
|
}
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
const ADJACENT_ALL = [
|
|
2
|
+
[0, -1, 0],
|
|
3
|
+
[0, 1, 0],
|
|
4
|
+
[0, 0, -1],
|
|
5
|
+
[0, 0, 1],
|
|
6
|
+
];
|
|
7
|
+
const ADJACENT_SINGLE_PLANE = [
|
|
8
|
+
[0, -1, 0],
|
|
9
|
+
[0, 1, 0],
|
|
10
|
+
];
|
|
11
|
+
const ADJACENT_IN = [
|
|
12
|
+
[0, -1, 0],
|
|
13
|
+
[0, 1, 0],
|
|
14
|
+
[0, 0, -1],
|
|
15
|
+
];
|
|
16
|
+
const ADJACENT_OUT = [
|
|
17
|
+
[0, -1, 0],
|
|
18
|
+
[0, 1, 0],
|
|
19
|
+
[0, 0, 1],
|
|
20
|
+
];
|
|
1
21
|
export default class RLEVoxelMap {
|
|
22
|
+
static copyMap(destination, source) {
|
|
23
|
+
for (const [index, row] of source.rows) {
|
|
24
|
+
destination.rows.set(index, structuredClone(row));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
2
27
|
constructor(width, height, depth = 1) {
|
|
3
28
|
this.rows = new Map();
|
|
4
29
|
this.height = 1;
|
|
@@ -6,14 +31,23 @@ export default class RLEVoxelMap {
|
|
|
6
31
|
this.depth = 1;
|
|
7
32
|
this.jMultiple = 1;
|
|
8
33
|
this.kMultiple = 1;
|
|
9
|
-
this.
|
|
10
|
-
this.defaultValue = 0;
|
|
34
|
+
this.numComps = 1;
|
|
11
35
|
this.pixelDataConstructor = Uint8Array;
|
|
36
|
+
this.updateScalarData = function (scalarData) {
|
|
37
|
+
scalarData.fill(0);
|
|
38
|
+
const callback = (index, rle, row) => {
|
|
39
|
+
const { start, end, value } = rle;
|
|
40
|
+
for (let i = start; i < end; i++) {
|
|
41
|
+
scalarData[index + i] = value;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
this.forEach(callback);
|
|
45
|
+
};
|
|
12
46
|
this.get = (index) => {
|
|
13
47
|
const i = index % this.jMultiple;
|
|
14
48
|
const j = (index - i) / this.jMultiple;
|
|
15
49
|
const rle = this.getRLE(i, j);
|
|
16
|
-
return rle?.value
|
|
50
|
+
return rle?.value ?? this.defaultValue;
|
|
17
51
|
};
|
|
18
52
|
this.getRun = (j, k) => {
|
|
19
53
|
const runIndex = j + k * this.height;
|
|
@@ -21,7 +55,7 @@ export default class RLEVoxelMap {
|
|
|
21
55
|
};
|
|
22
56
|
this.set = (index, value) => {
|
|
23
57
|
if (value === undefined) {
|
|
24
|
-
|
|
58
|
+
return;
|
|
25
59
|
}
|
|
26
60
|
const i = index % this.width;
|
|
27
61
|
const j = (index - i) / this.width;
|
|
@@ -50,7 +84,7 @@ export default class RLEVoxelMap {
|
|
|
50
84
|
const insertIndex = isAfter ? rleIndex + 1 : rleIndex;
|
|
51
85
|
const rlePrev = isAfter ? rle1 : rle0;
|
|
52
86
|
let rleNext = isAfter ? row[rleIndex + 1] : rle1;
|
|
53
|
-
if (rlePrev?.value === value && rlePrev
|
|
87
|
+
if (rlePrev?.value === value && rlePrev?.end === i) {
|
|
54
88
|
rlePrev.end++;
|
|
55
89
|
if (rleNext?.value === value && rleNext.start === i + 1) {
|
|
56
90
|
rlePrev.end = rleNext.end;
|
|
@@ -82,7 +116,7 @@ export default class RLEVoxelMap {
|
|
|
82
116
|
if (rleNext?.start === i && rleNext.end === i + 1) {
|
|
83
117
|
rleNext.value = value;
|
|
84
118
|
const nextnext = row[rleIndex + 1];
|
|
85
|
-
if (nextnext?.start == i + 1 && nextnext
|
|
119
|
+
if (nextnext?.start == i + 1 && nextnext.value === value) {
|
|
86
120
|
row.splice(rleIndex + 1, 1);
|
|
87
121
|
rleNext.end = nextnext.end;
|
|
88
122
|
}
|
|
@@ -111,6 +145,20 @@ export default class RLEVoxelMap {
|
|
|
111
145
|
this.jMultiple = width;
|
|
112
146
|
this.kMultiple = this.jMultiple * height;
|
|
113
147
|
}
|
|
148
|
+
static { this.getScalarData = function (ArrayType = Uint8ClampedArray) {
|
|
149
|
+
const scalarData = new ArrayType(this.frameSize);
|
|
150
|
+
this.map.updateScalarData(scalarData);
|
|
151
|
+
return scalarData;
|
|
152
|
+
}; }
|
|
153
|
+
toIJK(index) {
|
|
154
|
+
const i = index % this.jMultiple;
|
|
155
|
+
const j = ((index - i) / this.jMultiple) % this.height;
|
|
156
|
+
const k = Math.floor(index / this.kMultiple);
|
|
157
|
+
return [i, j, k];
|
|
158
|
+
}
|
|
159
|
+
toIndex([i, j, k]) {
|
|
160
|
+
return i + k * this.kMultiple + j * this.jMultiple;
|
|
161
|
+
}
|
|
114
162
|
getRLE(i, j, k = 0) {
|
|
115
163
|
const row = this.rows.get(j + k * this.height);
|
|
116
164
|
if (!row) {
|
|
@@ -120,6 +168,46 @@ export default class RLEVoxelMap {
|
|
|
120
168
|
const rle = row[index];
|
|
121
169
|
return i >= rle?.start ? rle : undefined;
|
|
122
170
|
}
|
|
171
|
+
has(index) {
|
|
172
|
+
const i = index % this.jMultiple;
|
|
173
|
+
const j = (index - i) / this.jMultiple;
|
|
174
|
+
const rle = this.getRLE(i, j);
|
|
175
|
+
return rle?.value !== undefined;
|
|
176
|
+
}
|
|
177
|
+
delete(index) {
|
|
178
|
+
const i = index % this.width;
|
|
179
|
+
const j = (index - i) / this.width;
|
|
180
|
+
const row = this.rows.get(j);
|
|
181
|
+
if (!row) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const rleIndex = this.findIndex(row, i);
|
|
185
|
+
const rle = row[rleIndex];
|
|
186
|
+
if (!rle || rle.start > i) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (rle.end === i + 1) {
|
|
190
|
+
rle.end--;
|
|
191
|
+
if (rle.start >= rle.end) {
|
|
192
|
+
row.splice(rleIndex, 1);
|
|
193
|
+
if (!row.length) {
|
|
194
|
+
this.rows.delete(j);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (rle.start === i) {
|
|
200
|
+
rle.start++;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const newRle = {
|
|
204
|
+
value: rle.value,
|
|
205
|
+
start: i + 1,
|
|
206
|
+
end: rle.end,
|
|
207
|
+
};
|
|
208
|
+
rle.end = i;
|
|
209
|
+
row.splice(rleIndex + 1, 0, newRle);
|
|
210
|
+
}
|
|
123
211
|
findIndex(row, i) {
|
|
124
212
|
for (let index = 0; index < row.length; index++) {
|
|
125
213
|
const { end: iEnd } = row[index];
|
|
@@ -129,6 +217,20 @@ export default class RLEVoxelMap {
|
|
|
129
217
|
}
|
|
130
218
|
return row.length;
|
|
131
219
|
}
|
|
220
|
+
forEach(callback, options) {
|
|
221
|
+
const rowModified = options?.rowModified;
|
|
222
|
+
for (const [baseIndex, row] of this.rows) {
|
|
223
|
+
const rowToUse = rowModified ? [...row] : row;
|
|
224
|
+
for (const rle of rowToUse) {
|
|
225
|
+
callback(baseIndex * this.width, rle, row);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
forEachRow(callback) {
|
|
230
|
+
for (const [baseIndex, row] of this.rows) {
|
|
231
|
+
callback(baseIndex * this.width, row);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
132
234
|
clear() {
|
|
133
235
|
this.rows.clear();
|
|
134
236
|
}
|
|
@@ -137,18 +239,18 @@ export default class RLEVoxelMap {
|
|
|
137
239
|
}
|
|
138
240
|
getPixelData(k = 0, pixelData) {
|
|
139
241
|
if (!pixelData) {
|
|
140
|
-
pixelData = new this.pixelDataConstructor(this.width * this.height * this.
|
|
242
|
+
pixelData = new this.pixelDataConstructor(this.width * this.height * this.numComps);
|
|
141
243
|
}
|
|
142
244
|
else {
|
|
143
245
|
pixelData.fill(0);
|
|
144
246
|
}
|
|
145
|
-
const { width, height,
|
|
247
|
+
const { width, height, numComps } = this;
|
|
146
248
|
for (let j = 0; j < height; j++) {
|
|
147
249
|
const row = this.getRun(j, k);
|
|
148
250
|
if (!row) {
|
|
149
251
|
continue;
|
|
150
252
|
}
|
|
151
|
-
if (
|
|
253
|
+
if (numComps === 1) {
|
|
152
254
|
for (const rle of row) {
|
|
153
255
|
const rowOffset = j * width;
|
|
154
256
|
const { start, end, value } = rle;
|
|
@@ -159,10 +261,10 @@ export default class RLEVoxelMap {
|
|
|
159
261
|
}
|
|
160
262
|
else {
|
|
161
263
|
for (const rle of row) {
|
|
162
|
-
const rowOffset = j * width *
|
|
264
|
+
const rowOffset = j * width * numComps;
|
|
163
265
|
const { start, end, value } = rle;
|
|
164
|
-
for (let i = start; i < end; i +=
|
|
165
|
-
for (let comp = 0; comp <
|
|
266
|
+
for (let i = start; i < end; i += numComps) {
|
|
267
|
+
for (let comp = 0; comp < numComps; comp++) {
|
|
166
268
|
pixelData[rowOffset + i + comp] = value[comp];
|
|
167
269
|
}
|
|
168
270
|
}
|
|
@@ -171,4 +273,103 @@ export default class RLEVoxelMap {
|
|
|
171
273
|
}
|
|
172
274
|
return pixelData;
|
|
173
275
|
}
|
|
276
|
+
floodFill(i, j, k, value, options) {
|
|
277
|
+
const rle = this.getRLE(i, j, k);
|
|
278
|
+
if (!rle) {
|
|
279
|
+
throw new Error(`Initial point ${i},${j},${k} isn't in the RLE`);
|
|
280
|
+
}
|
|
281
|
+
const stack = [[rle, j, k]];
|
|
282
|
+
const replaceValue = rle.value;
|
|
283
|
+
if (replaceValue === value) {
|
|
284
|
+
throw new Error(`source (${replaceValue}) and destination (${value}) are identical`);
|
|
285
|
+
}
|
|
286
|
+
return this.flood(stack, replaceValue, value, options);
|
|
287
|
+
}
|
|
288
|
+
flood(stack, sourceValue, value, options) {
|
|
289
|
+
let sum = 0;
|
|
290
|
+
const { planar = true, diagonals = true, singlePlane = false, } = options || {};
|
|
291
|
+
const childOptions = { planar, diagonals, singlePlane };
|
|
292
|
+
while (stack.length) {
|
|
293
|
+
const top = stack.pop();
|
|
294
|
+
const [current] = top;
|
|
295
|
+
if (current.value !== sourceValue) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
current.value = value;
|
|
299
|
+
sum += current.end - current.start;
|
|
300
|
+
const adjacents = this.findAdjacents(top, childOptions).filter((adjacent) => adjacent && adjacent[0].value === sourceValue);
|
|
301
|
+
stack.push(...adjacents);
|
|
302
|
+
}
|
|
303
|
+
return sum;
|
|
304
|
+
}
|
|
305
|
+
fillFrom(getter, boundsIJK) {
|
|
306
|
+
for (let k = boundsIJK[2][0]; k <= boundsIJK[2][1]; k++) {
|
|
307
|
+
for (let j = boundsIJK[1][0]; j <= boundsIJK[1][1]; j++) {
|
|
308
|
+
let rle;
|
|
309
|
+
let row;
|
|
310
|
+
for (let i = boundsIJK[0][0]; i <= boundsIJK[0][1]; i++) {
|
|
311
|
+
const value = getter(i, j, k);
|
|
312
|
+
if (value === undefined) {
|
|
313
|
+
rle = undefined;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (!row) {
|
|
317
|
+
row = [];
|
|
318
|
+
this.rows.set(j + k * this.height, row);
|
|
319
|
+
}
|
|
320
|
+
if (rle && rle.value !== value) {
|
|
321
|
+
rle = undefined;
|
|
322
|
+
}
|
|
323
|
+
if (!rle) {
|
|
324
|
+
rle = { start: i, end: i, value };
|
|
325
|
+
row.push(rle);
|
|
326
|
+
}
|
|
327
|
+
rle.end++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
findAdjacents(item, { diagonals = true, planar = true, singlePlane = false }) {
|
|
333
|
+
const [rle, j, k, adjacentsDelta] = item;
|
|
334
|
+
const { start, end } = rle;
|
|
335
|
+
const leftRle = start > 0 && this.getRLE(start - 1, j, k);
|
|
336
|
+
const rightRle = end < this.width && this.getRLE(end, j, k);
|
|
337
|
+
const range = diagonals
|
|
338
|
+
? [start > 0 ? start - 1 : start, end < this.width ? end + 1 : end]
|
|
339
|
+
: [start, end];
|
|
340
|
+
const adjacents = [];
|
|
341
|
+
if (leftRle) {
|
|
342
|
+
adjacents.push([leftRle, j, k]);
|
|
343
|
+
}
|
|
344
|
+
if (rightRle) {
|
|
345
|
+
adjacents.push([rightRle, j, k]);
|
|
346
|
+
}
|
|
347
|
+
for (const delta of adjacentsDelta ||
|
|
348
|
+
(singlePlane ? ADJACENT_SINGLE_PLANE : ADJACENT_ALL)) {
|
|
349
|
+
const [, delta1, delta2] = delta;
|
|
350
|
+
const testJ = delta1 + j;
|
|
351
|
+
const testK = delta2 + k;
|
|
352
|
+
if (testJ < 0 || testJ >= this.height) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (testK < 0 || testK >= this.depth) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const row = this.getRun(testJ, testK);
|
|
359
|
+
if (!row) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
for (const testRle of row) {
|
|
363
|
+
const newAdjacentDelta = adjacentsDelta ||
|
|
364
|
+
(singlePlane && ADJACENT_SINGLE_PLANE) ||
|
|
365
|
+
(planar && delta2 > 0 && ADJACENT_OUT) ||
|
|
366
|
+
(planar && delta2 < 0 && ADJACENT_IN) ||
|
|
367
|
+
ADJACENT_ALL;
|
|
368
|
+
if (!(testRle.end <= range[0] || testRle.start >= range[1])) {
|
|
369
|
+
adjacents.push([testRle, testJ, testK, newAdjacentDelta]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return adjacents;
|
|
374
|
+
}
|
|
174
375
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BoundsIJK, Point3, PixelDataTypedArray, IImage, RGB, CPUImageData, IVoxelManager, IRLEVoxelMap } from '../types';
|
|
1
|
+
import type { BoundsIJK, Point3, PixelDataTypedArray, IImage, RGB, CPUImageData, IVoxelManager, IRLEVoxelMap, Point2 } from '../types';
|
|
2
2
|
import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
3
3
|
export default class VoxelManager<T> {
|
|
4
4
|
modifiedSlices: Set<number>;
|
|
@@ -7,30 +7,45 @@ export default class VoxelManager<T> {
|
|
|
7
7
|
sourceVoxelManager: IVoxelManager<T>;
|
|
8
8
|
isInObject: (pointLPS: any, pointIJK: any) => boolean;
|
|
9
9
|
readonly dimensions: Point3;
|
|
10
|
-
numberOfComponents:
|
|
10
|
+
readonly numberOfComponents: any;
|
|
11
11
|
getCompleteScalarDataArray?: () => ArrayLike<number>;
|
|
12
12
|
setCompleteScalarDataArray?: (scalarData: ArrayLike<number>) => void;
|
|
13
13
|
getRange: () => [number, number];
|
|
14
14
|
private scalarData;
|
|
15
15
|
private _sliceDataCache;
|
|
16
|
+
readonly _id: string;
|
|
16
17
|
points: Set<number>;
|
|
17
18
|
width: number;
|
|
18
19
|
frameSize: number;
|
|
19
|
-
_get: (index: number) => T;
|
|
20
|
-
_set: (index: number, v: T) => boolean;
|
|
21
|
-
_getConstructor?: () => new (length: number) => PixelDataTypedArray;
|
|
20
|
+
readonly _get: (index: number) => T;
|
|
21
|
+
readonly _set: (index: number, v: T) => boolean;
|
|
22
|
+
readonly _getConstructor?: () => new (length: number) => PixelDataTypedArray;
|
|
22
23
|
_getScalarDataLength?: () => number;
|
|
23
|
-
_getScalarData?: () =>
|
|
24
|
+
_getScalarData?: () => ArrayLike<number>;
|
|
25
|
+
_updateScalarData?: (scalarData: ArrayLike<number>) => PixelDataTypedArray;
|
|
24
26
|
_getSliceData: (args: {
|
|
25
27
|
sliceIndex: number;
|
|
26
28
|
slicePlane: number;
|
|
27
29
|
}) => PixelDataTypedArray;
|
|
28
|
-
constructor(dimensions: any,
|
|
30
|
+
constructor(dimensions: any, options: {
|
|
31
|
+
_get: (index: number) => T;
|
|
32
|
+
_set?: (index: number, v: T) => boolean;
|
|
33
|
+
_getScalarData?: () => ArrayLike<number>;
|
|
34
|
+
_id?: string;
|
|
35
|
+
_updateScalarData?: (scalarData: ArrayLike<number>) => PixelDataTypedArray;
|
|
36
|
+
numberOfComponents?: number;
|
|
37
|
+
scalarData?: ArrayLike<number>;
|
|
38
|
+
_getConstructor?: () => new (length: number) => PixelDataTypedArray;
|
|
39
|
+
});
|
|
29
40
|
getAtIJK: (i: any, j: any, k: any) => T;
|
|
30
41
|
setAtIJK: (i: number, j: number, k: number, v: any) => boolean;
|
|
31
42
|
getAtIJKPoint: ([i, j, k]: [any, any, any]) => T;
|
|
32
43
|
setAtIJKPoint: ([i, j, k]: Point3, v: any) => void;
|
|
33
44
|
getAtIndex: (index: any) => T;
|
|
45
|
+
getMinMax(): {
|
|
46
|
+
min: any;
|
|
47
|
+
max: any;
|
|
48
|
+
};
|
|
34
49
|
setAtIndex: (index: any, v: any) => boolean;
|
|
35
50
|
toIJK(index: number): Point3;
|
|
36
51
|
getMiddleSliceData: () => PixelDataTypedArray;
|
|
@@ -47,8 +62,9 @@ export default class VoxelManager<T> {
|
|
|
47
62
|
isInObject?: (pointLPS: any, pointIJK: any) => boolean;
|
|
48
63
|
returnPoints?: boolean;
|
|
49
64
|
imageData?: vtkImageData | CPUImageData;
|
|
50
|
-
}) => any[];
|
|
51
|
-
|
|
65
|
+
}) => void | any[];
|
|
66
|
+
rleForEach(callback: any, options?: any): void;
|
|
67
|
+
getScalarData(storeScalarData?: boolean): PixelDataTypedArray;
|
|
52
68
|
setScalarData(newScalarData: PixelDataTypedArray): void;
|
|
53
69
|
getScalarDataLength(): number;
|
|
54
70
|
get sizeInBytes(): number;
|
|
@@ -96,13 +112,17 @@ export default class VoxelManager<T> {
|
|
|
96
112
|
static createHistoryVoxelManager<T>({ sourceVoxelManager, }: {
|
|
97
113
|
sourceVoxelManager: VoxelManager<T>;
|
|
98
114
|
}): VoxelManager<T>;
|
|
115
|
+
static createRLEHistoryVoxelManager<T>(sourceVoxelManager: VoxelManager<T>): VoxelManager<T>;
|
|
99
116
|
static createLazyVoxelManager<T>({ dimensions, planeFactory, }: {
|
|
100
117
|
dimensions: Point3;
|
|
101
118
|
planeFactory: (width: number, height: number) => T;
|
|
102
119
|
}): VoxelManager<T>;
|
|
103
|
-
static
|
|
120
|
+
static createRLEVolumeVoxelManager<T>({ dimensions, }: {
|
|
104
121
|
dimensions: Point3;
|
|
105
122
|
}): VoxelManager<T>;
|
|
123
|
+
static createRLEImageVoxelManager<T>({ dimensions, }: {
|
|
124
|
+
dimensions: Point2;
|
|
125
|
+
}): VoxelManager<T>;
|
|
106
126
|
static addInstanceToImage(image: IImage): void;
|
|
107
127
|
static: any;
|
|
108
128
|
}
|