@cornerstonejs/tools 3.10.31 → 3.11.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.
package/dist/esm/config.d.ts
CHANGED
|
@@ -3,12 +3,23 @@ import type { Types } from '@cornerstonejs/core';
|
|
|
3
3
|
import type { LabelmapSegmentationData } from './types/LabelmapTypes';
|
|
4
4
|
import type { SurfaceSegmentationData } from './types/SurfaceTypes';
|
|
5
5
|
import type SegmentationRepresentations from './enums/SegmentationRepresentations';
|
|
6
|
+
export type SurfacesInfo = {
|
|
7
|
+
id: string;
|
|
8
|
+
points: number[];
|
|
9
|
+
polys: number[];
|
|
10
|
+
segmentIndex: number;
|
|
11
|
+
};
|
|
6
12
|
export type PolySegConversionOptions = {
|
|
7
13
|
segmentIndices?: number[];
|
|
8
14
|
segmentationId?: string;
|
|
9
15
|
viewport?: Types.IStackViewport | Types.IVolumeViewport;
|
|
10
16
|
};
|
|
11
17
|
type ComputeRepresentationFn<T> = (segmentationId: string, options: PolySegConversionOptions) => Promise<T>;
|
|
18
|
+
export type SurfaceClipResult = {
|
|
19
|
+
points: number[];
|
|
20
|
+
lines: number[];
|
|
21
|
+
numberOfCells: number;
|
|
22
|
+
};
|
|
12
23
|
type PolySegAddOn = {
|
|
13
24
|
canComputeRequestedRepresentation: (segmentationId: string, representationType: SegmentationRepresentations) => boolean;
|
|
14
25
|
init: () => void;
|
|
@@ -16,6 +27,9 @@ type PolySegAddOn = {
|
|
|
16
27
|
computeLabelmapData: ComputeRepresentationFn<LabelmapSegmentationData>;
|
|
17
28
|
computeSurfaceData: ComputeRepresentationFn<SurfaceSegmentationData>;
|
|
18
29
|
updateSurfaceData: ComputeRepresentationFn<SurfaceSegmentationData>;
|
|
30
|
+
clipAndCacheSurfacesForViewport: (surfacesInfo: SurfacesInfo[], viewport: Types.IVolumeViewport) => Promise<Map<number, Map<string, SurfaceClipResult>>>;
|
|
31
|
+
extractContourData: (polyDataCache: Map<number, Map<string, SurfaceClipResult>>) => Map<number, SurfaceClipResult[]>;
|
|
32
|
+
createAndAddContourSegmentationsFromClippedSurfaces: (rawContourData: Map<number, SurfaceClipResult[]>, viewport: Types.IStackViewport | Types.IVolumeViewport, segmentationId: string) => Map<number, Set<string>>;
|
|
19
33
|
};
|
|
20
34
|
type AddOns = {
|
|
21
35
|
polySeg: PolySegAddOn;
|
package/dist/esm/config.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { getEnabledElementByViewportId } from '@cornerstonejs/core';
|
|
1
|
+
import { cache, getEnabledElementByViewportId, Enums, utilities, } from '@cornerstonejs/core';
|
|
2
2
|
import Representations from '../../../enums/SegmentationRepresentations';
|
|
3
3
|
import { handleContourSegmentation } from './contourHandler/handleContourSegmentation';
|
|
4
4
|
import { getSegmentation } from '../../../stateManagement/segmentation/getSegmentation';
|
|
5
5
|
import removeContourFromElement from './removeContourFromElement';
|
|
6
6
|
import { getPolySeg } from '../../../config';
|
|
7
7
|
import { computeAndAddRepresentation } from '../../../utilities/segmentation/computeAndAddRepresentation';
|
|
8
|
-
|
|
8
|
+
import { getUniqueSegmentIndices } from '../../../utilities/segmentation/getUniqueSegmentIndices';
|
|
9
|
+
import { getAnnotation } from '../../../stateManagement/annotation/annotationState';
|
|
10
|
+
import { vec3 } from 'gl-matrix';
|
|
11
|
+
const polySegConversionInProgressForViewportId = new Map();
|
|
9
12
|
const processedViewportSegmentations = new Map();
|
|
10
13
|
function removeRepresentation(viewportId, segmentationId, renderImmediate = false) {
|
|
11
14
|
const enabledElement = getEnabledElementByViewportId(viewportId);
|
|
@@ -26,13 +29,13 @@ async function render(viewport, contourRepresentation) {
|
|
|
26
29
|
return;
|
|
27
30
|
}
|
|
28
31
|
let contourData = segmentation.representationData[Representations.Contour];
|
|
32
|
+
const polySeg = getPolySeg();
|
|
29
33
|
if (!contourData &&
|
|
30
34
|
getPolySeg()?.canComputeRequestedRepresentation(segmentationId, Representations.Contour) &&
|
|
31
|
-
!
|
|
32
|
-
|
|
33
|
-
const polySeg = getPolySeg();
|
|
35
|
+
!polySegConversionInProgressForViewportId.get(viewport.id)) {
|
|
36
|
+
polySegConversionInProgressForViewportId.set(viewport.id, true);
|
|
34
37
|
contourData = await computeAndAddRepresentation(segmentationId, Representations.Contour, () => polySeg.computeContourData(segmentationId, { viewport }), () => undefined);
|
|
35
|
-
|
|
38
|
+
polySegConversionInProgressForViewportId.set(viewport.id, false);
|
|
36
39
|
}
|
|
37
40
|
else if (!contourData && !getPolySeg()) {
|
|
38
41
|
console.debug(`No contour data found for segmentationId ${segmentationId} and PolySeg add-on is not configured. Unable to convert from other representations to contour. Please register PolySeg using cornerstoneTools.init({ addons: { polySeg } }) to enable automatic conversion.`);
|
|
@@ -43,8 +46,89 @@ async function render(viewport, contourRepresentation) {
|
|
|
43
46
|
if (!contourData.geometryIds?.length) {
|
|
44
47
|
return;
|
|
45
48
|
}
|
|
49
|
+
let hasContourDataButNotMatchingViewport = false;
|
|
50
|
+
const viewportNormal = viewport.getCamera().viewPlaneNormal;
|
|
51
|
+
if (contourData.annotationUIDsMap) {
|
|
52
|
+
hasContourDataButNotMatchingViewport = !_checkContourNormalsMatchViewport(contourData.annotationUIDsMap, viewportNormal);
|
|
53
|
+
}
|
|
54
|
+
if (contourData.geometryIds.length > 0) {
|
|
55
|
+
hasContourDataButNotMatchingViewport = !_checkContourGeometryMatchViewport(contourData.geometryIds, viewportNormal);
|
|
56
|
+
}
|
|
57
|
+
const viewportProcessed = processedViewportSegmentations.get(viewport.id) || new Set();
|
|
58
|
+
if (hasContourDataButNotMatchingViewport &&
|
|
59
|
+
!polySegConversionInProgressForViewportId.get(viewport.id) &&
|
|
60
|
+
!viewportProcessed.has(segmentationId) &&
|
|
61
|
+
viewport.viewportStatus === Enums.ViewportStatus.RENDERED) {
|
|
62
|
+
polySegConversionInProgressForViewportId.set(viewport.id, true);
|
|
63
|
+
const segmentIndices = getUniqueSegmentIndices(segmentationId);
|
|
64
|
+
const surfacesInfo = await polySeg.computeSurfaceData(segmentationId, {
|
|
65
|
+
segmentIndices,
|
|
66
|
+
viewport,
|
|
67
|
+
});
|
|
68
|
+
const geometryIds = surfacesInfo.geometryIds;
|
|
69
|
+
const pointsAndPolys = [];
|
|
70
|
+
for (const geometryId of geometryIds.values()) {
|
|
71
|
+
const geometry = cache.getGeometry(geometryId);
|
|
72
|
+
const data = geometry.data;
|
|
73
|
+
pointsAndPolys.push({
|
|
74
|
+
points: data.points,
|
|
75
|
+
polys: data.polys,
|
|
76
|
+
segmentIndex: data.segmentIndex,
|
|
77
|
+
id: data.segmentIndex,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const polyDataCache = await polySeg.clipAndCacheSurfacesForViewport(pointsAndPolys, viewport);
|
|
81
|
+
const rawResults = polySeg.extractContourData(polyDataCache);
|
|
82
|
+
const annotationUIDsMap = polySeg.createAndAddContourSegmentationsFromClippedSurfaces(rawResults, viewport, segmentationId);
|
|
83
|
+
contourData.annotationUIDsMap = new Map([
|
|
84
|
+
...contourData.annotationUIDsMap,
|
|
85
|
+
...annotationUIDsMap,
|
|
86
|
+
]);
|
|
87
|
+
viewportProcessed.add(segmentationId);
|
|
88
|
+
processedViewportSegmentations.set(viewport.id, viewportProcessed);
|
|
89
|
+
polySegConversionInProgressForViewportId.set(viewport.id, false);
|
|
90
|
+
}
|
|
46
91
|
handleContourSegmentation(viewport, contourData.geometryIds, contourData.annotationUIDsMap, contourRepresentation);
|
|
47
92
|
}
|
|
93
|
+
function _checkContourGeometryMatchViewport(geometryIds, viewportNormal) {
|
|
94
|
+
const geometry = cache.getGeometry(geometryIds[0]);
|
|
95
|
+
if (!geometry) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const geometryData = geometry.data;
|
|
99
|
+
const contours = geometryData.contours;
|
|
100
|
+
const points = contours[0].points;
|
|
101
|
+
const point1 = points[0];
|
|
102
|
+
const point2 = points[1];
|
|
103
|
+
const point3 = points[2];
|
|
104
|
+
let normal = vec3.cross(vec3.create(), vec3.sub(vec3.create(), point2, point1), vec3.sub(vec3.create(), point3, point1));
|
|
105
|
+
normal = vec3.normalize(vec3.create(), normal);
|
|
106
|
+
const dotProduct = vec3.dot(normal, viewportNormal);
|
|
107
|
+
return Math.abs(dotProduct) > 0.9;
|
|
108
|
+
}
|
|
109
|
+
function _checkContourNormalsMatchViewport(annotationUIDsMap, viewportNormal) {
|
|
110
|
+
const annotationUIDs = Array.from(annotationUIDsMap.values())
|
|
111
|
+
.flat()
|
|
112
|
+
.map((uidSet) => Array.from(uidSet))
|
|
113
|
+
.flat();
|
|
114
|
+
const randomAnnotationUIDs = utilities.getRandomSampleFromArray(annotationUIDs, 3);
|
|
115
|
+
for (const annotationUID of randomAnnotationUIDs) {
|
|
116
|
+
const annotation = getAnnotation(annotationUID);
|
|
117
|
+
if (annotation?.metadata) {
|
|
118
|
+
if (!annotation.metadata.viewPlaneNormal) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const annotationNormal = annotation.metadata.viewPlaneNormal;
|
|
122
|
+
const dotProduct = Math.abs(viewportNormal[0] * annotationNormal[0] +
|
|
123
|
+
viewportNormal[1] * annotationNormal[1] +
|
|
124
|
+
viewportNormal[2] * annotationNormal[2]);
|
|
125
|
+
if (Math.abs(dotProduct - 1) > 0.01) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
48
132
|
export default {
|
|
49
133
|
render,
|
|
50
134
|
removeRepresentation,
|
|
@@ -56,6 +56,9 @@ async function calculateVolumeStatistics({ operationData, indices, unit, mode, }
|
|
|
56
56
|
origin: imageData.getOrigin(),
|
|
57
57
|
direction: imageData.getDirection(),
|
|
58
58
|
};
|
|
59
|
+
if (!imageInfo.scalarData?.length) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
59
62
|
const stats = await getWebWorkerManager().executeTask('compute', 'calculateSegmentsStatisticsVolume', {
|
|
60
63
|
segmentationInfo,
|
|
61
64
|
imageInfo,
|
|
@@ -121,7 +121,7 @@ export const getImageReferenceInfo = (segVolumeId, segImageIds) => {
|
|
|
121
121
|
const refImage = cache.getImage(refImageId);
|
|
122
122
|
const scalingModule = metaData.get('scalingModule', refImageId);
|
|
123
123
|
const modalityUnitOptions = {
|
|
124
|
-
isPreScaled: Boolean(refImage
|
|
124
|
+
isPreScaled: Boolean(refImage?.preScale?.scaled),
|
|
125
125
|
isSuvScaled: typeof scalingModule?.suvbw === 'number',
|
|
126
126
|
};
|
|
127
127
|
return { refImageId, modalityUnitOptions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.1",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"types": "./dist/esm/index.d.ts",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"canvas": "^3.1.0"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
|
-
"@cornerstonejs/core": "^3.
|
|
106
|
+
"@cornerstonejs/core": "^3.11.1",
|
|
107
107
|
"@kitware/vtk.js": "32.12.1",
|
|
108
108
|
"@types/d3-array": "^3.0.4",
|
|
109
109
|
"@types/d3-interpolate": "^3.0.1",
|
|
@@ -122,5 +122,5 @@
|
|
|
122
122
|
"type": "individual",
|
|
123
123
|
"url": "https://ohif.org/donate"
|
|
124
124
|
},
|
|
125
|
-
"gitHead": "
|
|
125
|
+
"gitHead": "9e71e94884bd1c8734911b98ac67a5676fcaaecb"
|
|
126
126
|
}
|