@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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Types } from '@cornerstonejs/core';
|
|
2
|
+
export declare const EPSILON = 1e-7;
|
|
3
|
+
export declare function vec2CrossZ(a: Types.Point2, b: Types.Point2): number;
|
|
4
|
+
export declare function pointsAreEqual(p1: Types.Point2, p2: Types.Point2): boolean;
|
|
5
|
+
export declare function robustSegmentIntersection(p1: Types.Point2, p2: Types.Point2, q1: Types.Point2, q2: Types.Point2): Types.Point2 | null;
|
|
6
|
+
export declare enum PolylineNodeType {
|
|
7
|
+
Vertex = 0,
|
|
8
|
+
Intersection = 1
|
|
9
|
+
}
|
|
10
|
+
export declare enum IntersectionDirection {
|
|
11
|
+
Entering = 0,
|
|
12
|
+
Exiting = 1,
|
|
13
|
+
Unknown = 2
|
|
14
|
+
}
|
|
15
|
+
export interface AugmentedPolyNode {
|
|
16
|
+
id: string;
|
|
17
|
+
coordinates: Types.Point2;
|
|
18
|
+
type: PolylineNodeType;
|
|
19
|
+
originalPolyIndex: 0 | 1;
|
|
20
|
+
originalVertexIndex?: number;
|
|
21
|
+
next: AugmentedPolyNode;
|
|
22
|
+
prev: AugmentedPolyNode;
|
|
23
|
+
isIntersection: boolean;
|
|
24
|
+
partnerNode?: AugmentedPolyNode;
|
|
25
|
+
intersectionDir?: IntersectionDirection;
|
|
26
|
+
intersectionInfo?: IntersectionInfo;
|
|
27
|
+
alpha?: number;
|
|
28
|
+
processedInPath?: boolean;
|
|
29
|
+
visited: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type IntersectionInfo = {
|
|
32
|
+
coord: Types.Point2;
|
|
33
|
+
seg1Idx: number;
|
|
34
|
+
seg2Idx: number;
|
|
35
|
+
alpha1: number;
|
|
36
|
+
alpha2: number;
|
|
37
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { utilities } from '@cornerstonejs/core';
|
|
2
|
+
import { vec2 } from 'gl-matrix';
|
|
3
|
+
export const EPSILON = 1e-7;
|
|
4
|
+
export function vec2CrossZ(a, b) {
|
|
5
|
+
return a[0] * b[1] - a[1] * b[0];
|
|
6
|
+
}
|
|
7
|
+
export function pointsAreEqual(p1, p2) {
|
|
8
|
+
return utilities.isEqual(p1, p2, EPSILON);
|
|
9
|
+
}
|
|
10
|
+
export function robustSegmentIntersection(p1, p2, q1, q2) {
|
|
11
|
+
const r = vec2.subtract(vec2.create(), p2, p1);
|
|
12
|
+
const s = vec2.subtract(vec2.create(), q2, q1);
|
|
13
|
+
const rxs = vec2CrossZ(r, s);
|
|
14
|
+
const qmp = vec2.subtract(vec2.create(), q1, p1);
|
|
15
|
+
const qmpxr = vec2CrossZ(qmp, r);
|
|
16
|
+
if (Math.abs(rxs) < EPSILON) {
|
|
17
|
+
if (Math.abs(qmpxr) < EPSILON) {
|
|
18
|
+
const rDotR = vec2.dot(r, r);
|
|
19
|
+
const sDotS = vec2.dot(s, s);
|
|
20
|
+
if (rDotR < EPSILON || sDotS < EPSILON) {
|
|
21
|
+
if (pointsAreEqual(p1, q1) || pointsAreEqual(p1, q2)) {
|
|
22
|
+
return p1;
|
|
23
|
+
}
|
|
24
|
+
if (pointsAreEqual(p2, q1) || pointsAreEqual(p2, q2)) {
|
|
25
|
+
return p2;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const t0 = vec2.dot(vec2.subtract(vec2.create(), q1, p1), r) / rDotR;
|
|
30
|
+
const t1 = vec2.dot(vec2.subtract(vec2.create(), q2, p1), r) / rDotR;
|
|
31
|
+
const u0 = vec2.dot(vec2.subtract(vec2.create(), p1, q1), s) / sDotS;
|
|
32
|
+
const u1 = vec2.dot(vec2.subtract(vec2.create(), p2, q1), s) / sDotS;
|
|
33
|
+
const isInRange = (t) => t >= -EPSILON && t <= 1 + EPSILON;
|
|
34
|
+
if (isInRange(t0)) {
|
|
35
|
+
const projectedPoint = vec2.scaleAndAdd(vec2.create(), p1, r, t0);
|
|
36
|
+
if (pointsAreEqual(q1, projectedPoint)) {
|
|
37
|
+
return q1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (isInRange(t1)) {
|
|
41
|
+
const projectedPoint = vec2.scaleAndAdd(vec2.create(), p1, r, t1);
|
|
42
|
+
if (pointsAreEqual(q2, projectedPoint)) {
|
|
43
|
+
return q2;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (isInRange(u0)) {
|
|
47
|
+
const projectedPoint = vec2.scaleAndAdd(vec2.create(), q1, s, u0);
|
|
48
|
+
if (pointsAreEqual(p1, projectedPoint)) {
|
|
49
|
+
return p1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (isInRange(u1)) {
|
|
53
|
+
const projectedPoint = vec2.scaleAndAdd(vec2.create(), q1, s, u1);
|
|
54
|
+
if (pointsAreEqual(p2, projectedPoint)) {
|
|
55
|
+
return p2;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const t = vec2CrossZ(qmp, s) / rxs;
|
|
62
|
+
const u = qmpxr / rxs;
|
|
63
|
+
if (t >= -EPSILON && t <= 1 + EPSILON && u >= -EPSILON && u <= 1 + EPSILON) {
|
|
64
|
+
return [p1[0] + t * r[0], p1[1] + t * r[1]];
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
export var PolylineNodeType;
|
|
69
|
+
(function (PolylineNodeType) {
|
|
70
|
+
PolylineNodeType[PolylineNodeType["Vertex"] = 0] = "Vertex";
|
|
71
|
+
PolylineNodeType[PolylineNodeType["Intersection"] = 1] = "Intersection";
|
|
72
|
+
})(PolylineNodeType || (PolylineNodeType = {}));
|
|
73
|
+
export var IntersectionDirection;
|
|
74
|
+
(function (IntersectionDirection) {
|
|
75
|
+
IntersectionDirection[IntersectionDirection["Entering"] = 0] = "Entering";
|
|
76
|
+
IntersectionDirection[IntersectionDirection["Exiting"] = 1] = "Exiting";
|
|
77
|
+
IntersectionDirection[IntersectionDirection["Unknown"] = 2] = "Unknown";
|
|
78
|
+
})(IntersectionDirection || (IntersectionDirection = {}));
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { vec2 } from 'gl-matrix';
|
|
2
|
+
import getSignedArea from './getSignedArea';
|
|
3
|
+
import { EPSILON, IntersectionDirection, pointsAreEqual, PolylineNodeType, robustSegmentIntersection, } from './robustSegmentIntersection';
|
|
4
|
+
import containsPoint from './containsPoint';
|
|
5
|
+
import arePolylinesIdentical from './arePolylinesIdentical';
|
|
6
|
+
export default function subtractPolylines(targetPolylineCoords, sourcePolylineCoordsInput) {
|
|
7
|
+
if (targetPolylineCoords.length < 3) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
if (sourcePolylineCoordsInput.length < 3) {
|
|
11
|
+
return [targetPolylineCoords.slice()];
|
|
12
|
+
}
|
|
13
|
+
const sourcePolylineCoords = sourcePolylineCoordsInput.slice();
|
|
14
|
+
if (arePolylinesIdentical(targetPolylineCoords, sourcePolylineCoordsInput)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const targetArea = getSignedArea(targetPolylineCoords);
|
|
18
|
+
const sourceArea = getSignedArea(sourcePolylineCoords);
|
|
19
|
+
if (Math.sign(targetArea) === Math.sign(sourceArea) &&
|
|
20
|
+
Math.abs(sourceArea) > EPSILON) {
|
|
21
|
+
sourcePolylineCoords.reverse();
|
|
22
|
+
}
|
|
23
|
+
const intersections = [];
|
|
24
|
+
for (let i = 0; i < targetPolylineCoords.length; i++) {
|
|
25
|
+
const p1 = targetPolylineCoords[i];
|
|
26
|
+
const p2 = targetPolylineCoords[(i + 1) % targetPolylineCoords.length];
|
|
27
|
+
for (let j = 0; j < sourcePolylineCoords.length; j++) {
|
|
28
|
+
const q1 = sourcePolylineCoords[j];
|
|
29
|
+
const q2 = sourcePolylineCoords[(j + 1) % sourcePolylineCoords.length];
|
|
30
|
+
const intersectPt = robustSegmentIntersection(p1, p2, q1, q2);
|
|
31
|
+
if (intersectPt) {
|
|
32
|
+
const lenP = Math.sqrt(vec2.squaredDistance(p1, p2));
|
|
33
|
+
const lenQ = Math.sqrt(vec2.squaredDistance(q1, q2));
|
|
34
|
+
intersections.push({
|
|
35
|
+
coord: intersectPt,
|
|
36
|
+
seg1Idx: i,
|
|
37
|
+
seg2Idx: j,
|
|
38
|
+
alpha1: lenP < EPSILON
|
|
39
|
+
? 0
|
|
40
|
+
: Math.sqrt(vec2.squaredDistance(p1, intersectPt)) / lenP,
|
|
41
|
+
alpha2: lenQ < EPSILON
|
|
42
|
+
? 0
|
|
43
|
+
: Math.sqrt(vec2.squaredDistance(q1, intersectPt)) / lenQ,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const buildAugmentedList = (polyCoords, polyIndex, allIntersections) => {
|
|
49
|
+
const augmentedList = [];
|
|
50
|
+
let nodeIdCounter = 0;
|
|
51
|
+
for (let i = 0; i < polyCoords.length; i++) {
|
|
52
|
+
const p1 = polyCoords[i];
|
|
53
|
+
augmentedList.push({
|
|
54
|
+
id: `${polyIndex}_v${nodeIdCounter++}`,
|
|
55
|
+
coordinates: p1,
|
|
56
|
+
type: PolylineNodeType.Vertex,
|
|
57
|
+
originalPolyIndex: polyIndex,
|
|
58
|
+
originalVertexIndex: i,
|
|
59
|
+
next: null,
|
|
60
|
+
prev: null,
|
|
61
|
+
isIntersection: false,
|
|
62
|
+
visited: false,
|
|
63
|
+
});
|
|
64
|
+
const segmentIntersections = allIntersections
|
|
65
|
+
.filter((isect) => (polyIndex === 0 ? isect.seg1Idx : isect.seg2Idx) === i)
|
|
66
|
+
.sort((a, b) => (polyIndex === 0 ? a.alpha1 : a.alpha2) -
|
|
67
|
+
(polyIndex === 0 ? b.alpha1 : b.alpha2));
|
|
68
|
+
for (const isect of segmentIntersections) {
|
|
69
|
+
if (augmentedList.length > 0 &&
|
|
70
|
+
pointsAreEqual(augmentedList[augmentedList.length - 1].coordinates, isect.coord)) {
|
|
71
|
+
if (!augmentedList[augmentedList.length - 1].isIntersection) {
|
|
72
|
+
augmentedList[augmentedList.length - 1].isIntersection = true;
|
|
73
|
+
augmentedList[augmentedList.length - 1].intersectionInfo = isect;
|
|
74
|
+
augmentedList[augmentedList.length - 1].alpha =
|
|
75
|
+
polyIndex === 0 ? isect.alpha1 : isect.alpha2;
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
augmentedList.push({
|
|
80
|
+
id: `${polyIndex}_i${nodeIdCounter++}`,
|
|
81
|
+
coordinates: isect.coord,
|
|
82
|
+
type: PolylineNodeType.Intersection,
|
|
83
|
+
originalPolyIndex: polyIndex,
|
|
84
|
+
next: null,
|
|
85
|
+
prev: null,
|
|
86
|
+
isIntersection: true,
|
|
87
|
+
visited: false,
|
|
88
|
+
alpha: polyIndex === 0 ? isect.alpha1 : isect.alpha2,
|
|
89
|
+
intersectionInfo: isect,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const finalList = [];
|
|
94
|
+
if (augmentedList.length > 0) {
|
|
95
|
+
finalList.push(augmentedList[0]);
|
|
96
|
+
for (let i = 1; i < augmentedList.length; i++) {
|
|
97
|
+
if (!pointsAreEqual(augmentedList[i].coordinates, finalList[finalList.length - 1].coordinates)) {
|
|
98
|
+
finalList.push(augmentedList[i]);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
if (augmentedList[i].isIntersection) {
|
|
102
|
+
finalList[finalList.length - 1].isIntersection = true;
|
|
103
|
+
finalList[finalList.length - 1].intersectionInfo =
|
|
104
|
+
augmentedList[i].intersectionInfo;
|
|
105
|
+
finalList[finalList.length - 1].alpha = augmentedList[i].alpha;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (finalList.length > 0) {
|
|
111
|
+
for (let i = 0; i < finalList.length; i++) {
|
|
112
|
+
finalList[i].next = finalList[(i + 1) % finalList.length];
|
|
113
|
+
finalList[i].prev =
|
|
114
|
+
finalList[(i - 1 + finalList.length) % finalList.length];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return finalList;
|
|
118
|
+
};
|
|
119
|
+
const targetAugmented = buildAugmentedList(targetPolylineCoords, 0, intersections);
|
|
120
|
+
const sourceAugmented = buildAugmentedList(sourcePolylineCoords, 1, intersections);
|
|
121
|
+
targetAugmented.forEach((tnode) => {
|
|
122
|
+
if (tnode.isIntersection) {
|
|
123
|
+
const tData = tnode.intersectionInfo;
|
|
124
|
+
const partner = sourceAugmented.find((snode) => snode.isIntersection &&
|
|
125
|
+
pointsAreEqual(snode.coordinates, tnode.coordinates) &&
|
|
126
|
+
snode.intersectionInfo.seg1Idx ===
|
|
127
|
+
tData.seg1Idx &&
|
|
128
|
+
snode.intersectionInfo.seg2Idx === tData.seg2Idx);
|
|
129
|
+
if (partner) {
|
|
130
|
+
tnode.partnerNode = partner;
|
|
131
|
+
partner.partnerNode = tnode;
|
|
132
|
+
const p_prev = tnode.prev.coordinates;
|
|
133
|
+
const p_curr = tnode.coordinates;
|
|
134
|
+
const p_next_source = partner.next.coordinates;
|
|
135
|
+
const v_target_arrival = vec2.subtract(vec2.create(), p_curr, p_prev);
|
|
136
|
+
const v_source_departure = vec2.subtract(vec2.create(), p_next_source, p_curr);
|
|
137
|
+
const midPrevTargetSeg = [
|
|
138
|
+
(tnode.prev.coordinates[0] + tnode.coordinates[0]) / 2,
|
|
139
|
+
(tnode.prev.coordinates[1] + tnode.coordinates[1]) / 2,
|
|
140
|
+
];
|
|
141
|
+
const prevSegMidpointInsideSource = containsPoint(sourcePolylineCoordsInput, midPrevTargetSeg);
|
|
142
|
+
if (prevSegMidpointInsideSource) {
|
|
143
|
+
tnode.intersectionDir = IntersectionDirection.Exiting;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
tnode.intersectionDir = IntersectionDirection.Entering;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
tnode.isIntersection = false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
targetAugmented.forEach((n) => delete n.intersectionInfo);
|
|
155
|
+
sourceAugmented.forEach((n) => delete n.intersectionInfo);
|
|
156
|
+
const resultPolylines = [];
|
|
157
|
+
for (let i = 0; i < targetAugmented.length; i++) {
|
|
158
|
+
const startNode = targetAugmented[i];
|
|
159
|
+
if (startNode.visited || startNode.isIntersection) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (containsPoint(sourcePolylineCoordsInput, startNode.coordinates)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const currentPathCoords = [];
|
|
166
|
+
let currentNode = startNode;
|
|
167
|
+
let onTargetList = true;
|
|
168
|
+
let safetyBreak = 0;
|
|
169
|
+
const maxIter = (targetAugmented.length + sourceAugmented.length) * 2;
|
|
170
|
+
do {
|
|
171
|
+
if (safetyBreak++ > maxIter) {
|
|
172
|
+
console.warn('Subtraction: Max iterations reached, possible infinite loop.');
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
currentNode.visited = true;
|
|
176
|
+
if (currentPathCoords.length === 0 ||
|
|
177
|
+
!pointsAreEqual(currentPathCoords[currentPathCoords.length - 1], currentNode.coordinates)) {
|
|
178
|
+
currentPathCoords.push(currentNode.coordinates);
|
|
179
|
+
}
|
|
180
|
+
if (currentNode.isIntersection) {
|
|
181
|
+
if (onTargetList) {
|
|
182
|
+
if (currentNode.intersectionDir === IntersectionDirection.Entering &&
|
|
183
|
+
currentNode.partnerNode) {
|
|
184
|
+
currentNode = currentNode.partnerNode;
|
|
185
|
+
onTargetList = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
if (currentNode.partnerNode) {
|
|
190
|
+
currentNode = currentNode.partnerNode;
|
|
191
|
+
onTargetList = true;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
console.warn('Subtraction: Intersection on source without partner.');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
currentNode = currentNode.next;
|
|
199
|
+
} while (currentNode !== startNode || !onTargetList);
|
|
200
|
+
if (currentPathCoords.length >= 3) {
|
|
201
|
+
if (pointsAreEqual(currentPathCoords[0], currentPathCoords[currentPathCoords.length - 1])) {
|
|
202
|
+
currentPathCoords.pop();
|
|
203
|
+
}
|
|
204
|
+
if (currentPathCoords.length >= 3) {
|
|
205
|
+
resultPolylines.push(currentPathCoords);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return resultPolylines;
|
|
210
|
+
}
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "3.
|
|
1
|
+
export declare const version = "3.26.0";
|
package/dist/esm/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '3.
|
|
1
|
+
export const version = '3.26.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.26.0",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"types": "./dist/esm/index.d.ts",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"canvas": "^3.1.0"
|
|
109
109
|
},
|
|
110
110
|
"peerDependencies": {
|
|
111
|
-
"@cornerstonejs/core": "^3.
|
|
111
|
+
"@cornerstonejs/core": "^3.26.0",
|
|
112
112
|
"@kitware/vtk.js": "32.12.1",
|
|
113
113
|
"@types/d3-array": "^3.0.4",
|
|
114
114
|
"@types/d3-interpolate": "^3.0.1",
|
|
@@ -127,5 +127,5 @@
|
|
|
127
127
|
"type": "individual",
|
|
128
128
|
"url": "https://ohif.org/donate"
|
|
129
129
|
},
|
|
130
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "a851443310a66ee3620011723733112128c2a7ba"
|
|
131
131
|
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { Types } from '@cornerstonejs/core';
|
|
2
|
-
export declare function unifyPolylineSets(polylinesSetA: Types.Point2[][], polylinesSetB: Types.Point2[][]): Types.Point2[][];
|
|
3
|
-
export declare function unifyMultiplePolylineSets(polylineSets: Types.Point2[][][]): Types.Point2[][];
|
|
4
|
-
export declare function unifyAnnotationPolylines(annotationsSetA: Array<{
|
|
5
|
-
data: {
|
|
6
|
-
contour: {
|
|
7
|
-
polyline: Types.Point3[];
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
}>, annotationsSetB: Array<{
|
|
11
|
-
data: {
|
|
12
|
-
contour: {
|
|
13
|
-
polyline: Types.Point3[];
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
}>, viewport: Types.IViewport): Types.Point2[][];
|
|
17
|
-
export declare function subtractPolylineSets(polylinesSetA: Types.Point2[][], polylinesSetB: Types.Point2[][]): Types.Point2[][];
|
|
18
|
-
export declare function subtractMultiplePolylineSets(basePolylineSet: Types.Point2[][], subtractorSets: Types.Point2[][][]): Types.Point2[][];
|
|
19
|
-
export declare function subtractAnnotationPolylines(baseAnnotations: Array<{
|
|
20
|
-
data: {
|
|
21
|
-
contour: {
|
|
22
|
-
polyline: Types.Point3[];
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
}>, subtractorAnnotations: Array<{
|
|
26
|
-
data: {
|
|
27
|
-
contour: {
|
|
28
|
-
polyline: Types.Point3[];
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
}>, viewport: Types.IViewport): Types.Point2[][];
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import * as math from '../math';
|
|
2
|
-
import { checkIntersection } from './sharedOperations';
|
|
3
|
-
export function unifyPolylineSets(polylinesSetA, polylinesSetB) {
|
|
4
|
-
const result = [];
|
|
5
|
-
const processedFromA = new Set();
|
|
6
|
-
const processedFromB = new Set();
|
|
7
|
-
for (let i = 0; i < polylinesSetA.length; i++) {
|
|
8
|
-
if (processedFromA.has(i)) {
|
|
9
|
-
continue;
|
|
10
|
-
}
|
|
11
|
-
const polylineA = polylinesSetA[i];
|
|
12
|
-
let merged = false;
|
|
13
|
-
for (let j = 0; j < polylinesSetB.length; j++) {
|
|
14
|
-
if (processedFromB.has(j)) {
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
const polylineB = polylinesSetB[j];
|
|
18
|
-
const intersection = checkIntersection(polylineA, polylineB);
|
|
19
|
-
if (intersection.hasIntersection && !intersection.isContourHole) {
|
|
20
|
-
const mergedPolyline = math.polyline.mergePolylines(polylineA, polylineB);
|
|
21
|
-
result.push(mergedPolyline);
|
|
22
|
-
processedFromA.add(i);
|
|
23
|
-
processedFromB.add(j);
|
|
24
|
-
merged = true;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (!merged) {
|
|
29
|
-
result.push([...polylineA]);
|
|
30
|
-
processedFromA.add(i);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
for (let j = 0; j < polylinesSetB.length; j++) {
|
|
34
|
-
if (!processedFromB.has(j)) {
|
|
35
|
-
result.push([...polylinesSetB[j]]);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
export function unifyMultiplePolylineSets(polylineSets) {
|
|
41
|
-
if (polylineSets.length === 0) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
if (polylineSets.length === 1) {
|
|
45
|
-
return polylineSets[0].map((polyline) => [...polyline]);
|
|
46
|
-
}
|
|
47
|
-
let result = polylineSets[0].map((polyline) => [...polyline]);
|
|
48
|
-
for (let i = 1; i < polylineSets.length; i++) {
|
|
49
|
-
result = unifyPolylineSets(result, polylineSets[i]);
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
export function unifyAnnotationPolylines(annotationsSetA, annotationsSetB, viewport) {
|
|
54
|
-
const polylinesSetA = annotationsSetA.map((annotation) => convertPolylineToCanvasSpace(annotation.data.contour.polyline, viewport));
|
|
55
|
-
const polylinesSetB = annotationsSetB.map((annotation) => convertPolylineToCanvasSpace(annotation.data.contour.polyline, viewport));
|
|
56
|
-
return unifyPolylineSets(polylinesSetA, polylinesSetB);
|
|
57
|
-
}
|
|
58
|
-
function convertPolylineToCanvasSpace(polyline, viewport) {
|
|
59
|
-
const numPoints = polyline.length;
|
|
60
|
-
const projectedPolyline = new Array(numPoints);
|
|
61
|
-
for (let i = 0; i < numPoints; i++) {
|
|
62
|
-
projectedPolyline[i] = viewport.worldToCanvas(polyline[i]);
|
|
63
|
-
}
|
|
64
|
-
return projectedPolyline;
|
|
65
|
-
}
|
|
66
|
-
export function subtractPolylineSets(polylinesSetA, polylinesSetB) {
|
|
67
|
-
const result = [];
|
|
68
|
-
const processedFromA = new Set();
|
|
69
|
-
for (let i = 0; i < polylinesSetA.length; i++) {
|
|
70
|
-
if (processedFromA.has(i)) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
let currentPolylines = [polylinesSetA[i]];
|
|
74
|
-
let wasSubtracted = false;
|
|
75
|
-
for (let j = 0; j < polylinesSetB.length; j++) {
|
|
76
|
-
const polylineB = polylinesSetB[j];
|
|
77
|
-
const newPolylines = [];
|
|
78
|
-
for (const currentPolyline of currentPolylines) {
|
|
79
|
-
const intersection = checkIntersection(currentPolyline, polylineB);
|
|
80
|
-
if (intersection.hasIntersection && !intersection.isContourHole) {
|
|
81
|
-
const subtractedPolylines = math.polyline.subtractPolylines(currentPolyline, polylineB);
|
|
82
|
-
newPolylines.push(...subtractedPolylines);
|
|
83
|
-
wasSubtracted = true;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
newPolylines.push(currentPolyline);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
currentPolylines = newPolylines;
|
|
90
|
-
}
|
|
91
|
-
result.push(...currentPolylines);
|
|
92
|
-
processedFromA.add(i);
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
export function subtractMultiplePolylineSets(basePolylineSet, subtractorSets) {
|
|
97
|
-
if (subtractorSets.length === 0) {
|
|
98
|
-
return basePolylineSet.map((polyline) => [...polyline]);
|
|
99
|
-
}
|
|
100
|
-
let result = basePolylineSet.map((polyline) => [...polyline]);
|
|
101
|
-
for (let i = 0; i < subtractorSets.length; i++) {
|
|
102
|
-
result = subtractPolylineSets(result, subtractorSets[i]);
|
|
103
|
-
}
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
export function subtractAnnotationPolylines(baseAnnotations, subtractorAnnotations, viewport) {
|
|
107
|
-
const basePolylines = baseAnnotations.map((annotation) => convertPolylineToCanvasSpace(annotation.data.contour.polyline, viewport));
|
|
108
|
-
const subtractorPolylines = subtractorAnnotations.map((annotation) => convertPolylineToCanvasSpace(annotation.data.contour.polyline, viewport));
|
|
109
|
-
return subtractPolylineSets(basePolylines, subtractorPolylines);
|
|
110
|
-
}
|