@cornerstonejs/tools 3.24.0 → 3.26.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/stateManagement/segmentation/utilities/convertContourHoles.d.ts +1 -0
- package/dist/esm/stateManagement/segmentation/utilities/convertContourHoles.js +66 -0
- package/dist/esm/stateManagement/segmentation/utilities/getAnnotationsUIDMapFromSegmentation.d.ts +1 -0
- package/dist/esm/stateManagement/segmentation/utilities/getAnnotationsUIDMapFromSegmentation.js +17 -0
- package/dist/esm/stateManagement/segmentation/utilities/index.d.ts +2 -0
- package/dist/esm/stateManagement/segmentation/utilities/index.js +3 -0
- package/dist/esm/stateManagement/segmentation/utilities/removeContourHoles.js +0 -1
- package/dist/esm/tools/annotation/CircleROITool.js +93 -53
- package/dist/esm/tools/annotation/SplineROITool.d.ts +2 -0
- package/dist/esm/tools/annotation/SplineROITool.js +18 -0
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.d.ts +1 -1
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +62 -46
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +8 -2
- package/dist/esm/types/index.d.ts +2 -1
- package/dist/esm/utilities/contourSegmentation/addPolylinesToSegmentation.d.ts +2 -1
- package/dist/esm/utilities/contourSegmentation/addPolylinesToSegmentation.js +3 -4
- package/dist/esm/utilities/contourSegmentation/areViewReferencesEqual.d.ts +2 -0
- package/dist/esm/utilities/contourSegmentation/areViewReferencesEqual.js +23 -0
- package/dist/esm/utilities/contourSegmentation/copyAnnotation.d.ts +3 -0
- package/dist/esm/utilities/contourSegmentation/copyAnnotation.js +86 -0
- package/dist/esm/utilities/contourSegmentation/getViewReferenceFromAnnotation.d.ts +3 -0
- package/dist/esm/utilities/contourSegmentation/getViewReferenceFromAnnotation.js +20 -0
- package/dist/esm/utilities/contourSegmentation/index.d.ts +7 -3
- package/dist/esm/utilities/contourSegmentation/index.js +7 -3
- package/dist/esm/utilities/contourSegmentation/logicalOperators.d.ts +21 -12
- package/dist/esm/utilities/contourSegmentation/logicalOperators.js +148 -43
- package/dist/esm/utilities/contourSegmentation/polylineInfoTypes.d.ts +9 -0
- package/dist/esm/utilities/contourSegmentation/polylineInfoTypes.js +0 -0
- package/dist/esm/utilities/contourSegmentation/polylineIntersect.d.ts +2 -0
- package/dist/esm/utilities/contourSegmentation/polylineIntersect.js +34 -0
- package/dist/esm/utilities/contourSegmentation/polylineSubtract.d.ts +6 -0
- package/dist/esm/utilities/contourSegmentation/polylineSubtract.js +67 -0
- package/dist/esm/utilities/contourSegmentation/polylineUnify.d.ts +6 -0
- package/dist/esm/utilities/contourSegmentation/polylineUnify.js +79 -0
- package/dist/esm/utilities/contourSegmentation/polylineXor.d.ts +2 -0
- package/dist/esm/utilities/contourSegmentation/polylineXor.js +41 -0
- package/dist/esm/utilities/contourSegmentation/sharedOperations.d.ts +2 -0
- package/dist/esm/utilities/contourSegmentation/sharedOperations.js +44 -0
- package/dist/esm/utilities/math/polyline/arePolylinesIdentical.d.ts +2 -0
- package/dist/esm/utilities/math/polyline/arePolylinesIdentical.js +53 -0
- package/dist/esm/utilities/math/polyline/combinePolyline.d.ts +1 -2
- package/dist/esm/utilities/math/polyline/combinePolyline.js +17 -47
- package/dist/esm/utilities/math/polyline/index.d.ts +5 -2
- package/dist/esm/utilities/math/polyline/index.js +5 -2
- package/dist/esm/utilities/math/polyline/intersectPolylines.d.ts +2 -0
- package/dist/esm/utilities/math/polyline/intersectPolylines.js +271 -0
- package/dist/esm/utilities/math/polyline/robustSegmentIntersection.d.ts +37 -0
- package/dist/esm/utilities/math/polyline/robustSegmentIntersection.js +78 -0
- package/dist/esm/utilities/math/polyline/subtractPolylines.d.ts +2 -0
- package/dist/esm/utilities/math/polyline/subtractPolylines.js +210 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +3 -3
- package/dist/esm/utilities/contourSegmentation/unifyPolylineSets.d.ts +0 -31
- package/dist/esm/utilities/contourSegmentation/unifyPolylineSets.js +0 -110
|
@@ -44,6 +44,7 @@ import type { SplineProps } from './SplineProps';
|
|
|
44
44
|
import type { BidirectionalData } from '../utilities/segmentation/createBidirectionalToolData';
|
|
45
45
|
import type { IBaseTool } from './IBaseTool';
|
|
46
46
|
import type { RepresentationStyle } from './../stateManagement/segmentation/SegmentationStyle';
|
|
47
|
+
import type { LogicalOperation } from '../utilities/contourSegmentation';
|
|
47
48
|
import type { LabelmapStyle, LabelmapSegmentationData, LabelmapSegmentationDataStack, LabelmapSegmentationDataVolume, BaseLabelmapStyle, InactiveLabelmapStyle } from './LabelmapTypes';
|
|
48
49
|
import type { SurfaceStyle, SurfaceSegmentationData, SurfaceStateStyles } from './SurfaceTypes';
|
|
49
|
-
export type { Annotation, Annotations, ContourAnnotationData, ContourAnnotation, ContourSegmentationAnnotationData, ContourSegmentationAnnotation, BidirectionalData, CanvasCoordinates, IAnnotationManager, InterpolationViewportData, ImageInterpolationData, GroupSpecificAnnotations, AnnotationState, AnnotationStyle, ToolSpecificAnnotationTypes, AnnotationGroupSelector, AnnotationRenderContext, PlanarBoundingBox, ToolProps, PublicToolProps, ToolConfiguration, EventTypes, IPoints, ITouchPoints, IDistance, IToolBinding, SetToolBindingsType, ToolOptionsType, InteractionTypes, ToolAction, IToolGroup, IToolClassReference, ISynchronizerEventHandler, ToolHandle, AnnotationHandle, TextBoxHandle, Segmentation, SegmentationRepresentation, SegmentationState, RepresentationData, RepresentationsData, SVGCursorDescriptor, SVGPoint, CINETypes, BoundsIJK, SVGDrawingHelper, FloodFillResult, FloodFillGetter, FloodFillOptions, ContourSegmentationData, ISculptToolShape, Statistics, NamedStatistics, LabelmapToolOperationData, LabelmapToolOperationDataStack, LabelmapToolOperationDataVolume, CardinalSplineProps, ClosestControlPoint, ClosestPoint, ClosestSplinePoint, ControlPointInfo, ISpline, SplineCurveSegment, SplineLineSegment, SplineProps, IBaseTool, RepresentationStyle, Segment, SegmentationPublicInput, LabelmapStyle, ContourStyle, SurfaceStyle, SurfaceSegmentationData, SurfaceStateStyles, LabelmapSegmentationData, LabelmapSegmentationDataStack, LabelmapSegmentationDataVolume, BaseLabelmapStyle, InactiveLabelmapStyle, };
|
|
50
|
+
export type { Annotation, Annotations, ContourAnnotationData, ContourAnnotation, ContourSegmentationAnnotationData, ContourSegmentationAnnotation, BidirectionalData, CanvasCoordinates, IAnnotationManager, InterpolationViewportData, ImageInterpolationData, GroupSpecificAnnotations, AnnotationState, AnnotationStyle, ToolSpecificAnnotationTypes, AnnotationGroupSelector, AnnotationRenderContext, PlanarBoundingBox, ToolProps, PublicToolProps, ToolConfiguration, EventTypes, IPoints, ITouchPoints, IDistance, IToolBinding, SetToolBindingsType, ToolOptionsType, InteractionTypes, ToolAction, IToolGroup, IToolClassReference, ISynchronizerEventHandler, ToolHandle, AnnotationHandle, TextBoxHandle, Segmentation, SegmentationRepresentation, SegmentationState, RepresentationData, RepresentationsData, SVGCursorDescriptor, SVGPoint, CINETypes, BoundsIJK, SVGDrawingHelper, FloodFillResult, FloodFillGetter, FloodFillOptions, ContourSegmentationData, ISculptToolShape, Statistics, NamedStatistics, LabelmapToolOperationData, LabelmapToolOperationDataStack, LabelmapToolOperationDataVolume, CardinalSplineProps, ClosestControlPoint, ClosestPoint, ClosestSplinePoint, ControlPointInfo, ISpline, SplineCurveSegment, SplineLineSegment, SplineProps, IBaseTool, RepresentationStyle, Segment, SegmentationPublicInput, LabelmapStyle, ContourStyle, SurfaceStyle, SurfaceSegmentationData, SurfaceStateStyles, LabelmapSegmentationData, LabelmapSegmentationDataStack, LabelmapSegmentationDataVolume, BaseLabelmapStyle, InactiveLabelmapStyle, LogicalOperation, };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
|
-
|
|
2
|
+
import type { PolylineInfoWorld } from './polylineInfoTypes';
|
|
3
|
+
export default function addPolylinesToSegmentation(viewport: Types.IViewport, annotationUIDsMap: Map<number, Set<string>>, segmentationId: string, polylinesInfo: PolylineInfoWorld[], segmentIndex: number): Map<number, Set<string>>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { utilities } from '@cornerstonejs/core';
|
|
2
2
|
import { addAnnotation } from '../../stateManagement';
|
|
3
3
|
const DEFAULT_CONTOUR_SEG_TOOLNAME = 'PlanarFreehandContourSegmentationTool';
|
|
4
|
-
export default function addPolylinesToSegmentation(viewport, segmentationId,
|
|
5
|
-
|
|
6
|
-
polylines.forEach((polyline) => {
|
|
4
|
+
export default function addPolylinesToSegmentation(viewport, annotationUIDsMap, segmentationId, polylinesInfo, segmentIndex) {
|
|
5
|
+
polylinesInfo.forEach(({ polyline, viewReference }) => {
|
|
7
6
|
if (polyline.length < 3) {
|
|
8
7
|
return;
|
|
9
8
|
}
|
|
@@ -28,7 +27,7 @@ export default function addPolylinesToSegmentation(viewport, segmentationId, pol
|
|
|
28
27
|
isVisible: true,
|
|
29
28
|
metadata: {
|
|
30
29
|
toolName: DEFAULT_CONTOUR_SEG_TOOLNAME,
|
|
31
|
-
...
|
|
30
|
+
...viewReference,
|
|
32
31
|
},
|
|
33
32
|
};
|
|
34
33
|
addAnnotation(contourSegmentationAnnotation, viewport.element);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function areViewReferencesEqual(a, b) {
|
|
2
|
+
if (!a || !b) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (a.FrameOfReferenceUID !== b.FrameOfReferenceUID) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (a.referencedImageId !== b.referencedImageId) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (!a.viewPlaneNormal || !b.viewPlaneNormal) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (a.viewPlaneNormal.length !== b.viewPlaneNormal.length) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
for (let i = 0; i < a.viewPlaneNormal.length; i++) {
|
|
18
|
+
if (a.viewPlaneNormal[i] !== b.viewPlaneNormal[i]) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ContourSegmentationAnnotation } from '../../types';
|
|
2
|
+
export declare function copyAnnotation(annotation: ContourSegmentationAnnotation, segmentationId: string, segmentIndex: number): ContourSegmentationAnnotation;
|
|
3
|
+
export declare function copyContourSegment(segmentationId: string, segmentIndex: number, targetSegmentationId: string, targetSegmentIndex: number): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { utilities } from '@cornerstonejs/core';
|
|
2
|
+
import { addAnnotation, getAnnotation } from '../../stateManagement';
|
|
3
|
+
import { getViewportAssociatedToSegmentation } from '../../stateManagement/segmentation/utilities/getViewportAssociatedToSegmentation';
|
|
4
|
+
import { getToolGroupForViewport } from '../../store/ToolGroupManager';
|
|
5
|
+
import { getAnnotationsUIDMapFromSegmentation } from '../../stateManagement/segmentation/utilities/getAnnotationsUIDMapFromSegmentation';
|
|
6
|
+
export function copyAnnotation(annotation, segmentationId, segmentIndex) {
|
|
7
|
+
const newAnnotation = {
|
|
8
|
+
annotationUID: utilities.uuidv4(),
|
|
9
|
+
data: {
|
|
10
|
+
contour: {
|
|
11
|
+
closed: true,
|
|
12
|
+
polyline: [],
|
|
13
|
+
},
|
|
14
|
+
segmentation: {
|
|
15
|
+
segmentationId,
|
|
16
|
+
segmentIndex,
|
|
17
|
+
},
|
|
18
|
+
handles: {},
|
|
19
|
+
},
|
|
20
|
+
handles: {},
|
|
21
|
+
highlighted: false,
|
|
22
|
+
autoGenerated: false,
|
|
23
|
+
invalidated: false,
|
|
24
|
+
isLocked: false,
|
|
25
|
+
isVisible: true,
|
|
26
|
+
metadata: {
|
|
27
|
+
...annotation.metadata,
|
|
28
|
+
toolName: annotation.metadata.toolName,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
newAnnotation.data.segmentation.segmentationId = segmentationId;
|
|
32
|
+
newAnnotation.data.segmentation.segmentIndex = segmentIndex;
|
|
33
|
+
if (annotation.data.contour?.polyline) {
|
|
34
|
+
newAnnotation.data.contour.polyline = [...annotation.data.contour.polyline];
|
|
35
|
+
}
|
|
36
|
+
if (annotation.data.handles?.points) {
|
|
37
|
+
newAnnotation.data.handles.points = annotation.data.handles.points.map((point) => [...point]);
|
|
38
|
+
}
|
|
39
|
+
return newAnnotation;
|
|
40
|
+
}
|
|
41
|
+
export function copyContourSegment(segmentationId, segmentIndex, targetSegmentationId, targetSegmentIndex) {
|
|
42
|
+
const annotationUIDsMap = getAnnotationsUIDMapFromSegmentation(segmentationId);
|
|
43
|
+
const targetAnnotationUIDsMap = getAnnotationsUIDMapFromSegmentation(targetSegmentationId);
|
|
44
|
+
if (!annotationUIDsMap || !targetAnnotationUIDsMap) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!annotationUIDsMap?.has(segmentIndex)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const annotationUIDs = annotationUIDsMap.get(segmentIndex);
|
|
51
|
+
const viewport = getViewportAssociatedToSegmentation(targetSegmentationId);
|
|
52
|
+
if (!viewport) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const toolGroup = getToolGroupForViewport(viewport.id);
|
|
56
|
+
const copyContourAnnotation = (annotation) => {
|
|
57
|
+
const newAnnotation = copyAnnotation(annotation, targetSegmentationId, targetSegmentIndex);
|
|
58
|
+
if (toolGroup) {
|
|
59
|
+
const instance = toolGroup.getToolInstance(annotation.metadata.toolName);
|
|
60
|
+
if (instance) {
|
|
61
|
+
if (typeof instance.isSplineAnnotation === 'function' &&
|
|
62
|
+
instance.isSplineAnnotation(annotation)) {
|
|
63
|
+
instance.createSplineObjectFromType(newAnnotation, annotation.data.spline.type);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
addAnnotation(newAnnotation, viewport.element);
|
|
68
|
+
newAnnotationsUID.add(newAnnotation.annotationUID);
|
|
69
|
+
return newAnnotation;
|
|
70
|
+
};
|
|
71
|
+
const newAnnotationsUID = new Set();
|
|
72
|
+
for (const annotationUID of annotationUIDs) {
|
|
73
|
+
const annotation = getAnnotation(annotationUID);
|
|
74
|
+
const newAnnotation = copyContourAnnotation(annotation);
|
|
75
|
+
if (annotation?.childAnnotationUIDs) {
|
|
76
|
+
newAnnotation.childAnnotationUIDs = [];
|
|
77
|
+
for (const childAnnotationUID of annotation.childAnnotationUIDs) {
|
|
78
|
+
const childAnnotation = getAnnotation(childAnnotationUID);
|
|
79
|
+
const newChildAnnotation = copyContourAnnotation(childAnnotation);
|
|
80
|
+
newChildAnnotation.parentAnnotationUID = newAnnotation.annotationUID;
|
|
81
|
+
newAnnotation.childAnnotationUIDs.push(newChildAnnotation.annotationUID);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
targetAnnotationUIDsMap.set(targetSegmentIndex, newAnnotationsUID);
|
|
86
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function getViewReferenceFromAnnotation(annotation) {
|
|
2
|
+
const { metadata } = annotation;
|
|
3
|
+
if (!metadata) {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
const { FrameOfReferenceUID, referencedImageId, referencedImageURI, multiSliceReference, cameraFocalPoint, viewPlaneNormal, viewUp, sliceIndex, volumeId, bounds, } = metadata;
|
|
7
|
+
const viewReference = {
|
|
8
|
+
FrameOfReferenceUID,
|
|
9
|
+
referencedImageId,
|
|
10
|
+
referencedImageURI,
|
|
11
|
+
multiSliceReference,
|
|
12
|
+
cameraFocalPoint,
|
|
13
|
+
viewPlaneNormal,
|
|
14
|
+
viewUp,
|
|
15
|
+
sliceIndex,
|
|
16
|
+
volumeId,
|
|
17
|
+
bounds,
|
|
18
|
+
};
|
|
19
|
+
return viewReference;
|
|
20
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import areSameSegment from './areSameSegment';
|
|
2
2
|
import convertContourSegmentationAnnotation from './convertContourSegmentation';
|
|
3
|
-
import {
|
|
3
|
+
import { copyAnnotation, copyContourSegment } from './copyAnnotation';
|
|
4
|
+
export * from './logicalOperators';
|
|
4
5
|
export { default as isContourSegmentationAnnotation } from './isContourSegmentationAnnotation';
|
|
5
6
|
export { addContourSegmentationAnnotation } from './addContourSegmentationAnnotation';
|
|
6
7
|
export { removeContourSegmentationAnnotation } from './removeContourSegmentationAnnotation';
|
|
@@ -8,5 +9,8 @@ export { findAllIntersectingContours } from './getIntersectingAnnotations';
|
|
|
8
9
|
export { processMultipleIntersections } from './mergeMultipleAnnotations';
|
|
9
10
|
export { contourSegmentationOperation } from './contourSegmentationOperation';
|
|
10
11
|
export * from './sharedOperations';
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
12
|
+
export { areSameSegment, convertContourSegmentationAnnotation, copyContourSegment, copyAnnotation, };
|
|
13
|
+
export * from './polylineUnify';
|
|
14
|
+
export * from './polylineSubtract';
|
|
15
|
+
export * from './polylineIntersect';
|
|
16
|
+
export * from './polylineXor';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import areSameSegment from './areSameSegment';
|
|
2
2
|
import convertContourSegmentationAnnotation from './convertContourSegmentation';
|
|
3
|
-
import {
|
|
3
|
+
import { copyAnnotation, copyContourSegment } from './copyAnnotation';
|
|
4
|
+
export * from './logicalOperators';
|
|
4
5
|
export { default as isContourSegmentationAnnotation } from './isContourSegmentationAnnotation';
|
|
5
6
|
export { addContourSegmentationAnnotation } from './addContourSegmentationAnnotation';
|
|
6
7
|
export { removeContourSegmentationAnnotation } from './removeContourSegmentationAnnotation';
|
|
@@ -8,5 +9,8 @@ export { findAllIntersectingContours } from './getIntersectingAnnotations';
|
|
|
8
9
|
export { processMultipleIntersections } from './mergeMultipleAnnotations';
|
|
9
10
|
export { contourSegmentationOperation } from './contourSegmentationOperation';
|
|
10
11
|
export * from './sharedOperations';
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
12
|
+
export { areSameSegment, convertContourSegmentationAnnotation, copyContourSegment, copyAnnotation, };
|
|
13
|
+
export * from './polylineUnify';
|
|
14
|
+
export * from './polylineSubtract';
|
|
15
|
+
export * from './polylineIntersect';
|
|
16
|
+
export * from './polylineXor';
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
export type SegmentInfo = {
|
|
2
|
+
segmentationId: string;
|
|
3
|
+
segmentIndex: number;
|
|
4
|
+
label?: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
};
|
|
7
|
+
export type OperatorOptions = SegmentInfo;
|
|
8
|
+
export declare enum LogicalOperation {
|
|
9
|
+
Union = 0,
|
|
10
|
+
Subtract = 1,
|
|
11
|
+
Intersect = 2,
|
|
12
|
+
XOR = 3,
|
|
13
|
+
Copy = 4,
|
|
14
|
+
Delete = 5
|
|
15
|
+
}
|
|
16
|
+
export declare function add(segment1: SegmentInfo, segment2: SegmentInfo, options: OperatorOptions): void;
|
|
17
|
+
export declare function subtract(segment1: SegmentInfo, segment2: SegmentInfo, options: OperatorOptions): void;
|
|
18
|
+
export declare function intersect(segment1: SegmentInfo, segment2: SegmentInfo, options: OperatorOptions): void;
|
|
19
|
+
export declare function xor(segment1: SegmentInfo, segment2: SegmentInfo, options: OperatorOptions): void;
|
|
20
|
+
export declare function copy(segment: SegmentInfo, options: OperatorOptions): void;
|
|
21
|
+
export declare function deleteOperation(segment: SegmentInfo): void;
|
|
@@ -1,75 +1,180 @@
|
|
|
1
|
-
import { getAnnotation } from '../../stateManagement';
|
|
1
|
+
import { getAnnotation, removeAnnotation } from '../../stateManagement';
|
|
2
2
|
import { convertContourPolylineToCanvasSpace, convertContourPolylineToWorld, } from './sharedOperations';
|
|
3
|
-
import { subtractPolylineSets, unifyPolylineSets } from './unifyPolylineSets';
|
|
4
3
|
import addPolylinesToSegmentation from './addPolylinesToSegmentation';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
|
|
5
|
+
import { copyContourSegment } from './copyAnnotation';
|
|
6
|
+
import { removeContourSegmentationAnnotation } from './removeContourSegmentationAnnotation';
|
|
7
|
+
import { getViewportAssociatedToSegmentation } from '../../stateManagement/segmentation/utilities/getViewportAssociatedToSegmentation';
|
|
8
|
+
import { unifyPolylineSets } from './polylineUnify';
|
|
9
|
+
import { subtractPolylineSets } from './polylineSubtract';
|
|
10
|
+
import { intersectPolylinesSets } from './polylineIntersect';
|
|
11
|
+
import { xorPolylinesSets } from './polylineXor';
|
|
12
|
+
import { getViewReferenceFromAnnotation } from './getViewReferenceFromAnnotation';
|
|
13
|
+
export var LogicalOperation;
|
|
14
|
+
(function (LogicalOperation) {
|
|
15
|
+
LogicalOperation[LogicalOperation["Union"] = 0] = "Union";
|
|
16
|
+
LogicalOperation[LogicalOperation["Subtract"] = 1] = "Subtract";
|
|
17
|
+
LogicalOperation[LogicalOperation["Intersect"] = 2] = "Intersect";
|
|
18
|
+
LogicalOperation[LogicalOperation["XOR"] = 3] = "XOR";
|
|
19
|
+
LogicalOperation[LogicalOperation["Copy"] = 4] = "Copy";
|
|
20
|
+
LogicalOperation[LogicalOperation["Delete"] = 5] = "Delete";
|
|
21
|
+
})(LogicalOperation || (LogicalOperation = {}));
|
|
22
|
+
function getPolylinesInfoWorld(contourRepresentationData, segmentIndex) {
|
|
23
|
+
const polylinesInfo = [];
|
|
24
|
+
const { annotationUIDsMap } = contourRepresentationData || {};
|
|
25
|
+
if (!annotationUIDsMap?.has(segmentIndex)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
8
28
|
const annotationUIDs = annotationUIDsMap.get(segmentIndex);
|
|
9
29
|
for (const annotationUID of annotationUIDs) {
|
|
10
30
|
const annotation = getAnnotation(annotationUID);
|
|
11
|
-
const { polyline } = annotation.data
|
|
12
|
-
|
|
13
|
-
|
|
31
|
+
const { polyline } = annotation.data.contour;
|
|
32
|
+
polylinesInfo.push({
|
|
33
|
+
polyline,
|
|
34
|
+
viewReference: getViewReferenceFromAnnotation(annotation),
|
|
35
|
+
});
|
|
14
36
|
}
|
|
15
|
-
return
|
|
37
|
+
return polylinesInfo;
|
|
16
38
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
39
|
+
function extractPolylinesInCanvasSpace(viewport, segment1, segment2) {
|
|
40
|
+
const segmentation1 = getSegmentation(segment1.segmentationId);
|
|
41
|
+
const segmentation2 = getSegmentation(segment2.segmentationId);
|
|
42
|
+
if (!segmentation1 || !segmentation2) {
|
|
19
43
|
return;
|
|
20
44
|
}
|
|
21
|
-
if (!
|
|
45
|
+
if (!segmentation1.representationData.Contour ||
|
|
46
|
+
!segmentation2.representationData.Contour) {
|
|
22
47
|
return;
|
|
23
48
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!annotationUIDsMap) {
|
|
49
|
+
const polyLinesInfoWorld1 = getPolylinesInfoWorld(segmentation1.representationData.Contour, segment1.segmentIndex);
|
|
50
|
+
const polyLinesInfoWorld2 = getPolylinesInfoWorld(segmentation2.representationData.Contour, segment2.segmentIndex);
|
|
51
|
+
if (!polyLinesInfoWorld1 || !polyLinesInfoWorld2) {
|
|
28
52
|
return;
|
|
29
53
|
}
|
|
30
|
-
|
|
54
|
+
const polyLinesInfoCanvas1 = polyLinesInfoWorld1.map(({ polyline, viewReference }) => {
|
|
55
|
+
return {
|
|
56
|
+
polyline: convertContourPolylineToCanvasSpace(polyline, viewport),
|
|
57
|
+
viewReference,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
const polyLinesInfoCanvas2 = polyLinesInfoWorld2.map(({ polyline, viewReference }) => {
|
|
61
|
+
return {
|
|
62
|
+
polyline: convertContourPolylineToCanvasSpace(polyline, viewport),
|
|
63
|
+
viewReference,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
return { polyLinesInfoCanvas1, polyLinesInfoCanvas2 };
|
|
67
|
+
}
|
|
68
|
+
function addSegmentInSegmentation(segmentation, { segmentIndex, label, color }) {
|
|
69
|
+
if (!segmentation?.segments) {
|
|
31
70
|
return;
|
|
32
71
|
}
|
|
33
|
-
|
|
72
|
+
segmentation.segments[segmentIndex] = {
|
|
73
|
+
active: false,
|
|
74
|
+
locked: false,
|
|
75
|
+
label,
|
|
76
|
+
segmentIndex,
|
|
77
|
+
cachedStats: {},
|
|
78
|
+
color,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function removeAnnotations(annotationUIDList) {
|
|
82
|
+
annotationUIDList.forEach((annotationUID) => {
|
|
83
|
+
const annotation = getAnnotation(annotationUID);
|
|
84
|
+
removeAnnotation(annotationUID);
|
|
85
|
+
removeContourSegmentationAnnotation(annotation);
|
|
86
|
+
});
|
|
87
|
+
annotationUIDList.clear();
|
|
88
|
+
}
|
|
89
|
+
function applyLogicalOperation(segment1, segment2, options, operation) {
|
|
90
|
+
const viewport = getViewportAssociatedToSegmentation(segment1.segmentationId);
|
|
91
|
+
if (!viewport) {
|
|
34
92
|
return;
|
|
35
93
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const polyLinesCanvas1 = polyLines1.map((polyline) => convertContourPolylineToCanvasSpace(polyline, viewport));
|
|
39
|
-
const polyLinesCanvas2 = polyLines2.map((polyline) => convertContourPolylineToCanvasSpace(polyline, viewport));
|
|
40
|
-
return { polyLinesCanvas1, polyLinesCanvas2 };
|
|
41
|
-
}
|
|
42
|
-
export function addition(viewport, segmentation, segmentIndex1, segmentIndex2, { name, segmentIndex, color }) {
|
|
43
|
-
const { polyLinesCanvas1, polyLinesCanvas2 } = extractPolylines(viewport, segmentation, segmentIndex1, segmentIndex2) ||
|
|
44
|
-
{};
|
|
45
|
-
if (!polyLinesCanvas1 || polyLinesCanvas2) {
|
|
94
|
+
const { polyLinesInfoCanvas1, polyLinesInfoCanvas2 } = extractPolylinesInCanvasSpace(viewport, segment1, segment2) || {};
|
|
95
|
+
if (!polyLinesInfoCanvas1 || !polyLinesInfoCanvas2) {
|
|
46
96
|
return;
|
|
47
97
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
98
|
+
let polylinesMerged;
|
|
99
|
+
switch (operation) {
|
|
100
|
+
case LogicalOperation.Union:
|
|
101
|
+
polylinesMerged = unifyPolylineSets(polyLinesInfoCanvas1, polyLinesInfoCanvas2);
|
|
102
|
+
break;
|
|
103
|
+
case LogicalOperation.Subtract:
|
|
104
|
+
polylinesMerged = subtractPolylineSets(polyLinesInfoCanvas1, polyLinesInfoCanvas2);
|
|
105
|
+
break;
|
|
106
|
+
case LogicalOperation.Intersect:
|
|
107
|
+
polylinesMerged = intersectPolylinesSets(polyLinesInfoCanvas1, polyLinesInfoCanvas2);
|
|
108
|
+
break;
|
|
109
|
+
case LogicalOperation.XOR:
|
|
110
|
+
polylinesMerged = xorPolylinesSets(polyLinesInfoCanvas1, polyLinesInfoCanvas2);
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
polylinesMerged = unifyPolylineSets(polyLinesInfoCanvas1, polyLinesInfoCanvas2);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
const polyLinesWorld = polylinesMerged.map(({ polyline, viewReference }) => {
|
|
117
|
+
return {
|
|
118
|
+
polyline: convertContourPolylineToWorld(polyline, viewport),
|
|
119
|
+
viewReference,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
const resultSegment = options;
|
|
123
|
+
const segmentation = getSegmentation(resultSegment.segmentationId);
|
|
124
|
+
const segmentIndex = resultSegment.segmentIndex;
|
|
125
|
+
const color = resultSegment.color;
|
|
126
|
+
const label = resultSegment.label;
|
|
51
127
|
const contourRepresentationData = segmentation.representationData
|
|
52
128
|
.Contour;
|
|
53
129
|
const { annotationUIDsMap } = contourRepresentationData;
|
|
54
130
|
if (!annotationUIDsMap) {
|
|
55
131
|
return;
|
|
56
132
|
}
|
|
57
|
-
|
|
133
|
+
if (segment1.segmentationId === resultSegment.segmentationId &&
|
|
134
|
+
segment1.segmentIndex === segmentIndex) {
|
|
135
|
+
const existingAnnotationUIDs = annotationUIDsMap.get(segmentIndex);
|
|
136
|
+
if (existingAnnotationUIDs) {
|
|
137
|
+
removeAnnotations(existingAnnotationUIDs);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
addPolylinesToSegmentation(viewport, annotationUIDsMap, segmentation.segmentationId, polyLinesWorld, segmentIndex);
|
|
141
|
+
addSegmentInSegmentation(segmentation, { segmentIndex, color, label });
|
|
142
|
+
}
|
|
143
|
+
export function add(segment1, segment2, options) {
|
|
144
|
+
applyLogicalOperation(segment1, segment2, options, LogicalOperation.Union);
|
|
145
|
+
}
|
|
146
|
+
export function subtract(segment1, segment2, options) {
|
|
147
|
+
applyLogicalOperation(segment1, segment2, options, LogicalOperation.Subtract);
|
|
148
|
+
}
|
|
149
|
+
export function intersect(segment1, segment2, options) {
|
|
150
|
+
applyLogicalOperation(segment1, segment2, options, LogicalOperation.Intersect);
|
|
58
151
|
}
|
|
59
|
-
export function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
152
|
+
export function xor(segment1, segment2, options) {
|
|
153
|
+
applyLogicalOperation(segment1, segment2, options, LogicalOperation.XOR);
|
|
154
|
+
}
|
|
155
|
+
export function copy(segment, options) {
|
|
156
|
+
copyContourSegment(segment.segmentationId, segment.segmentIndex, options.segmentationId, options.segmentIndex);
|
|
157
|
+
}
|
|
158
|
+
export function deleteOperation(segment) {
|
|
159
|
+
const segmentation = getSegmentation(segment.segmentationId);
|
|
160
|
+
if (!segmentation) {
|
|
161
|
+
console.log('No active segmentation detected');
|
|
63
162
|
return;
|
|
64
163
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const { annotationUIDsMap } =
|
|
164
|
+
if (!segmentation.representationData.Contour) {
|
|
165
|
+
console.log('No contour representation found');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const representationData = segmentation.representationData.Contour;
|
|
169
|
+
const { annotationUIDsMap } = representationData;
|
|
71
170
|
if (!annotationUIDsMap) {
|
|
171
|
+
console.log('No annotation map found');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!annotationUIDsMap.has(segment.segmentIndex)) {
|
|
175
|
+
console.log('Segmentation index has no annotations');
|
|
72
176
|
return;
|
|
73
177
|
}
|
|
74
|
-
annotationUIDsMap.
|
|
178
|
+
const annotationUIDList = annotationUIDsMap.get(segment.segmentIndex);
|
|
179
|
+
removeAnnotations(annotationUIDList);
|
|
75
180
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
export type PolylineInfoWorld = {
|
|
3
|
+
polyline: Types.Point3[];
|
|
4
|
+
viewReference: Types.ViewReference;
|
|
5
|
+
};
|
|
6
|
+
export type PolylineInfoCanvas = {
|
|
7
|
+
polyline: Types.Point2[];
|
|
8
|
+
viewReference: Types.ViewReference;
|
|
9
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { checkIntersection, cleanupPolylines } from './sharedOperations';
|
|
2
|
+
import { intersectPolylines } from '../math/polyline';
|
|
3
|
+
import arePolylinesIdentical from '../math/polyline/arePolylinesIdentical';
|
|
4
|
+
import { areViewReferencesEqual } from './areViewReferencesEqual';
|
|
5
|
+
export function intersectPolylinesSets(set1, set2) {
|
|
6
|
+
if (!set1.length || !set2.length) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const result = [];
|
|
10
|
+
for (const polyA of set1) {
|
|
11
|
+
for (const polyB of set2) {
|
|
12
|
+
if (!areViewReferencesEqual(polyA.viewReference, polyB.viewReference)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (arePolylinesIdentical(polyA.polyline, polyB.polyline)) {
|
|
16
|
+
result.push({ ...polyA });
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const intersection = checkIntersection(polyA.polyline, polyB.polyline);
|
|
20
|
+
if (intersection.hasIntersection && !intersection.isContourHole) {
|
|
21
|
+
const intersectionRegions = cleanupPolylines(intersectPolylines(polyA.polyline, polyB.polyline));
|
|
22
|
+
if (intersectionRegions && intersectionRegions.length > 0) {
|
|
23
|
+
intersectionRegions.forEach((region) => {
|
|
24
|
+
result.push({
|
|
25
|
+
polyline: region,
|
|
26
|
+
viewReference: polyA.viewReference,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
import type { PolylineInfoCanvas } from './polylineInfoTypes';
|
|
3
|
+
import type { ContourSegmentationAnnotation } from '../../types';
|
|
4
|
+
export declare function subtractPolylineSets(polylinesSetA: PolylineInfoCanvas[], polylinesSetB: PolylineInfoCanvas[]): PolylineInfoCanvas[];
|
|
5
|
+
export declare function subtractMultiplePolylineSets(basePolylineSet: PolylineInfoCanvas[], subtractorSets: PolylineInfoCanvas[][]): PolylineInfoCanvas[];
|
|
6
|
+
export declare function subtractAnnotationPolylines(baseAnnotations: ContourSegmentationAnnotation[], subtractorAnnotations: ContourSegmentationAnnotation[], viewport: Types.IViewport): PolylineInfoCanvas[];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as math from '../math';
|
|
2
|
+
import { checkIntersection, cleanupPolylines, convertContourPolylineToCanvasSpace, removeDuplicatePoints, } from './sharedOperations';
|
|
3
|
+
import arePolylinesIdentical from '../math/polyline/arePolylinesIdentical';
|
|
4
|
+
import { getViewReferenceFromAnnotation } from './getViewReferenceFromAnnotation';
|
|
5
|
+
import { areViewReferencesEqual } from './areViewReferencesEqual';
|
|
6
|
+
export function subtractPolylineSets(polylinesSetA, polylinesSetB) {
|
|
7
|
+
const result = [];
|
|
8
|
+
for (let i = 0; i < polylinesSetA.length; i++) {
|
|
9
|
+
let currentPolylines = [polylinesSetA[i]];
|
|
10
|
+
for (let j = 0; j < polylinesSetB.length; j++) {
|
|
11
|
+
const polylineB = polylinesSetB[j];
|
|
12
|
+
const newPolylines = [];
|
|
13
|
+
for (const currentPolyline of currentPolylines) {
|
|
14
|
+
if (!areViewReferencesEqual(currentPolyline.viewReference, polylineB.viewReference)) {
|
|
15
|
+
newPolylines.push(currentPolyline);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (arePolylinesIdentical(currentPolyline.polyline, polylineB.polyline)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const intersection = checkIntersection(currentPolyline.polyline, polylineB.polyline);
|
|
22
|
+
if (intersection.hasIntersection && !intersection.isContourHole) {
|
|
23
|
+
const subtractedPolylines = cleanupPolylines(math.polyline.subtractPolylines(currentPolyline.polyline, polylineB.polyline));
|
|
24
|
+
for (const subtractedPolyline of subtractedPolylines) {
|
|
25
|
+
const cleaned = removeDuplicatePoints(subtractedPolyline);
|
|
26
|
+
if (cleaned.length >= 3) {
|
|
27
|
+
newPolylines.push({
|
|
28
|
+
polyline: cleaned,
|
|
29
|
+
viewReference: currentPolyline.viewReference,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
newPolylines.push({
|
|
36
|
+
polyline: currentPolyline.polyline,
|
|
37
|
+
viewReference: currentPolyline.viewReference,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
currentPolylines = newPolylines;
|
|
42
|
+
}
|
|
43
|
+
result.push(...currentPolylines);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
export function subtractMultiplePolylineSets(basePolylineSet, subtractorSets) {
|
|
48
|
+
if (subtractorSets.length === 0) {
|
|
49
|
+
return [...basePolylineSet];
|
|
50
|
+
}
|
|
51
|
+
let result = [...basePolylineSet];
|
|
52
|
+
for (let i = 0; i < subtractorSets.length; i++) {
|
|
53
|
+
result = subtractPolylineSets(result, subtractorSets[i]);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
export function subtractAnnotationPolylines(baseAnnotations, subtractorAnnotations, viewport) {
|
|
58
|
+
const basePolylines = baseAnnotations.map((annotation) => ({
|
|
59
|
+
polyline: convertContourPolylineToCanvasSpace(annotation.data.contour.polyline, viewport),
|
|
60
|
+
viewReference: getViewReferenceFromAnnotation(annotation),
|
|
61
|
+
}));
|
|
62
|
+
const subtractorPolylines = subtractorAnnotations.map((annotation) => ({
|
|
63
|
+
polyline: convertContourPolylineToCanvasSpace(annotation.data.contour.polyline, viewport),
|
|
64
|
+
viewReference: getViewReferenceFromAnnotation(annotation),
|
|
65
|
+
}));
|
|
66
|
+
return subtractPolylineSets(basePolylines, subtractorPolylines);
|
|
67
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
import type { PolylineInfoCanvas } from './polylineInfoTypes';
|
|
3
|
+
import type { ContourSegmentationAnnotation } from '../../types';
|
|
4
|
+
export declare function unifyPolylineSets(polylinesSetA: PolylineInfoCanvas[], polylinesSetB: PolylineInfoCanvas[]): PolylineInfoCanvas[];
|
|
5
|
+
export declare function unifyMultiplePolylineSets(polylineSets: PolylineInfoCanvas[][]): PolylineInfoCanvas[];
|
|
6
|
+
export declare function unifyAnnotationPolylines(annotationsSetA: ContourSegmentationAnnotation[], annotationsSetB: ContourSegmentationAnnotation[], viewport: Types.IViewport): PolylineInfoCanvas[];
|