@cornerstonejs/tools 1.5.0 → 1.7.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/cjs/tools/annotation/AngleTool.js +9 -4
- package/dist/cjs/tools/annotation/AngleTool.js.map +1 -1
- package/dist/cjs/tools/annotation/BidirectionalTool.js +9 -6
- package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
- package/dist/cjs/tools/annotation/CircleROITool.js +16 -10
- package/dist/cjs/tools/annotation/CircleROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/EllipticalROITool.js +11 -6
- package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/LengthTool.js +7 -4
- package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +10 -7
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/RectangleROITool.js +10 -7
- package/dist/cjs/tools/annotation/RectangleROITool.js.map +1 -1
- package/dist/cjs/tools/base/AnnotationDisplayTool.js +4 -23
- package/dist/cjs/tools/base/AnnotationDisplayTool.js.map +1 -1
- package/dist/cjs/utilities/calibrateImageSpacing.d.ts +1 -1
- package/dist/cjs/utilities/calibrateImageSpacing.js +7 -7
- package/dist/cjs/utilities/calibrateImageSpacing.js.map +1 -1
- package/dist/cjs/utilities/getCalibratedUnits.d.ts +6 -0
- package/dist/cjs/utilities/getCalibratedUnits.js +35 -0
- package/dist/cjs/utilities/getCalibratedUnits.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +2 -1
- package/dist/cjs/utilities/index.js +3 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/roundNumber.d.ts +2 -0
- package/dist/cjs/utilities/roundNumber.js +25 -0
- package/dist/cjs/utilities/roundNumber.js.map +1 -0
- package/dist/esm/tools/annotation/AngleTool.js +9 -4
- package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
- package/dist/esm/tools/annotation/BidirectionalTool.js +9 -6
- package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
- package/dist/esm/tools/annotation/CircleROITool.js +16 -10
- package/dist/esm/tools/annotation/CircleROITool.js.map +1 -1
- package/dist/esm/tools/annotation/EllipticalROITool.js +11 -6
- package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
- package/dist/esm/tools/annotation/LengthTool.js +7 -4
- package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +10 -7
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/esm/tools/annotation/RectangleROITool.js +10 -7
- package/dist/esm/tools/annotation/RectangleROITool.js.map +1 -1
- package/dist/esm/tools/base/AnnotationDisplayTool.js +5 -24
- package/dist/esm/tools/base/AnnotationDisplayTool.js.map +1 -1
- package/dist/esm/utilities/calibrateImageSpacing.d.ts +1 -1
- package/dist/esm/utilities/calibrateImageSpacing.js +8 -8
- package/dist/esm/utilities/calibrateImageSpacing.js.map +1 -1
- package/dist/esm/utilities/getCalibratedUnits.d.ts +6 -0
- package/dist/esm/utilities/getCalibratedUnits.js +29 -0
- package/dist/esm/utilities/getCalibratedUnits.js.map +1 -0
- package/dist/esm/utilities/index.d.ts +2 -1
- package/dist/esm/utilities/index.js +2 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/roundNumber.d.ts +2 -0
- package/dist/esm/utilities/roundNumber.js +23 -0
- package/dist/esm/utilities/roundNumber.js.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/tools/annotation/AngleTool.ts +14 -9
- package/src/tools/annotation/BidirectionalTool.ts +13 -8
- package/src/tools/annotation/CircleROITool.ts +23 -10
- package/src/tools/annotation/EllipticalROITool.ts +15 -6
- package/src/tools/annotation/LengthTool.ts +10 -5
- package/src/tools/annotation/PlanarFreehandROITool.ts +15 -7
- package/src/tools/annotation/RectangleROITool.ts +13 -7
- package/src/tools/base/AnnotationDisplayTool.ts +5 -57
- package/src/utilities/calibrateImageSpacing.ts +11 -14
- package/src/utilities/getCalibratedUnits.ts +66 -0
- package/src/utilities/index.ts +2 -0
- package/src/utilities/roundNumber.ts +34 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"main": "dist/umd/index.js",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cornerstonejs/core": "^1.
|
|
30
|
+
"@cornerstonejs/core": "^1.7.0",
|
|
31
31
|
"lodash.clonedeep": "4.5.0",
|
|
32
32
|
"lodash.get": "^4.4.2"
|
|
33
33
|
},
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"type": "individual",
|
|
51
51
|
"url": "https://ohif.org/donate"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "757fe06aab211f0bd506c40f262264e90290b702"
|
|
54
54
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
|
|
17
17
|
import * as lineSegment from '../../utilities/math/line';
|
|
18
18
|
import angleBetweenLines from '../../utilities/math/angle/angleBetweenLines';
|
|
19
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
19
20
|
|
|
20
21
|
import {
|
|
21
22
|
drawHandles as drawHandlesSvg,
|
|
@@ -200,7 +201,6 @@ class AngleTool extends AnnotationTool {
|
|
|
200
201
|
const [point1, point2, point3] = data.handles.points;
|
|
201
202
|
const canvasPoint1 = viewport.worldToCanvas(point1);
|
|
202
203
|
const canvasPoint2 = viewport.worldToCanvas(point2);
|
|
203
|
-
const canvasPoint3 = viewport.worldToCanvas(point3);
|
|
204
204
|
|
|
205
205
|
const line1 = {
|
|
206
206
|
start: {
|
|
@@ -213,6 +213,17 @@ class AngleTool extends AnnotationTool {
|
|
|
213
213
|
},
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
+
const distanceToPoint = lineSegment.distanceToPoint(
|
|
217
|
+
[line1.start.x, line1.start.y],
|
|
218
|
+
[line1.end.x, line1.end.y],
|
|
219
|
+
[canvasCoords[0], canvasCoords[1]]
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (distanceToPoint <= proximity) return true;
|
|
223
|
+
if (!point3) return false;
|
|
224
|
+
|
|
225
|
+
const canvasPoint3 = viewport.worldToCanvas(point3);
|
|
226
|
+
|
|
216
227
|
const line2 = {
|
|
217
228
|
start: {
|
|
218
229
|
x: canvasPoint2[0],
|
|
@@ -224,19 +235,13 @@ class AngleTool extends AnnotationTool {
|
|
|
224
235
|
},
|
|
225
236
|
};
|
|
226
237
|
|
|
227
|
-
const distanceToPoint = lineSegment.distanceToPoint(
|
|
228
|
-
[line1.start.x, line1.start.y],
|
|
229
|
-
[line1.end.x, line1.end.y],
|
|
230
|
-
[canvasCoords[0], canvasCoords[1]]
|
|
231
|
-
);
|
|
232
|
-
|
|
233
238
|
const distanceToPoint2 = lineSegment.distanceToPoint(
|
|
234
239
|
[line2.start.x, line2.start.y],
|
|
235
240
|
[line2.end.x, line2.end.y],
|
|
236
241
|
[canvasCoords[0], canvasCoords[1]]
|
|
237
242
|
);
|
|
238
243
|
|
|
239
|
-
if (
|
|
244
|
+
if (distanceToPoint2 <= proximity) {
|
|
240
245
|
return true;
|
|
241
246
|
}
|
|
242
247
|
|
|
@@ -783,7 +788,7 @@ class AngleTool extends AnnotationTool {
|
|
|
783
788
|
return;
|
|
784
789
|
}
|
|
785
790
|
|
|
786
|
-
const textLines = [`${angle
|
|
791
|
+
const textLines = [`${roundNumber(angle)} ${String.fromCharCode(176)}`];
|
|
787
792
|
|
|
788
793
|
return textLines;
|
|
789
794
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { vec2, vec3
|
|
1
|
+
import { vec2, vec3 } from 'gl-matrix';
|
|
2
2
|
import {
|
|
3
3
|
getEnabledElement,
|
|
4
4
|
triggerEvent,
|
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
} from '@cornerstonejs/core';
|
|
8
8
|
import type { Types } from '@cornerstonejs/core';
|
|
9
9
|
|
|
10
|
+
import {
|
|
11
|
+
getCalibratedLengthUnits,
|
|
12
|
+
getCalibratedScale,
|
|
13
|
+
} from '../../utilities/getCalibratedUnits';
|
|
14
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
10
15
|
import { AnnotationTool } from '../base';
|
|
11
16
|
import throttle from '../../utilities/throttle';
|
|
12
17
|
import {
|
|
@@ -1252,8 +1257,8 @@ class BidirectionalTool extends AnnotationTool {
|
|
|
1252
1257
|
// spaceBetweenSlices & pixelSpacing &
|
|
1253
1258
|
// magnitude in each direction? Otherwise, this is "px"?
|
|
1254
1259
|
const textLines = [
|
|
1255
|
-
`L: ${length
|
|
1256
|
-
`W: ${width
|
|
1260
|
+
`L: ${roundNumber(length)} ${unit}`,
|
|
1261
|
+
`W: ${roundNumber(width)} ${unit}`,
|
|
1257
1262
|
];
|
|
1258
1263
|
|
|
1259
1264
|
return textLines;
|
|
@@ -1291,10 +1296,10 @@ class BidirectionalTool extends AnnotationTool {
|
|
|
1291
1296
|
continue;
|
|
1292
1297
|
}
|
|
1293
1298
|
|
|
1294
|
-
const { imageData, dimensions
|
|
1295
|
-
|
|
1296
|
-
const dist1 = this._calculateLength(worldPos1, worldPos2);
|
|
1297
|
-
const dist2 = this._calculateLength(worldPos3, worldPos4);
|
|
1299
|
+
const { imageData, dimensions } = image;
|
|
1300
|
+
const scale = getCalibratedScale(image);
|
|
1301
|
+
const dist1 = this._calculateLength(worldPos1, worldPos2) / scale;
|
|
1302
|
+
const dist2 = this._calculateLength(worldPos3, worldPos4) / scale;
|
|
1298
1303
|
const length = dist1 > dist2 ? dist1 : dist2;
|
|
1299
1304
|
const width = dist1 > dist2 ? dist2 : dist1;
|
|
1300
1305
|
|
|
@@ -1310,7 +1315,7 @@ class BidirectionalTool extends AnnotationTool {
|
|
|
1310
1315
|
cachedStats[targetId] = {
|
|
1311
1316
|
length,
|
|
1312
1317
|
width,
|
|
1313
|
-
unit:
|
|
1318
|
+
unit: getCalibratedLengthUnits(null, image),
|
|
1314
1319
|
};
|
|
1315
1320
|
}
|
|
1316
1321
|
|
|
@@ -9,6 +9,13 @@ import {
|
|
|
9
9
|
} from '@cornerstonejs/core';
|
|
10
10
|
import type { Types } from '@cornerstonejs/core';
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
getCalibratedLengthUnits,
|
|
14
|
+
getCalibratedAreaUnits,
|
|
15
|
+
getCalibratedScale,
|
|
16
|
+
getCalibratedAspect,
|
|
17
|
+
} from '../../utilities/getCalibratedUnits';
|
|
18
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
12
19
|
import throttle from '../../utilities/throttle';
|
|
13
20
|
import {
|
|
14
21
|
addAnnotation,
|
|
@@ -876,27 +883,27 @@ class CircleROITool extends AnnotationTool {
|
|
|
876
883
|
if (radius) {
|
|
877
884
|
const radiusLine = isEmptyArea
|
|
878
885
|
? `Radius: Oblique not supported`
|
|
879
|
-
: `Radius: ${radius
|
|
886
|
+
: `Radius: ${roundNumber(radius)} ${radiusUnit}`;
|
|
880
887
|
textLines.push(radiusLine);
|
|
881
888
|
}
|
|
882
889
|
|
|
883
890
|
if (area) {
|
|
884
891
|
const areaLine = isEmptyArea
|
|
885
892
|
? `Area: Oblique not supported`
|
|
886
|
-
: `Area: ${area
|
|
893
|
+
: `Area: ${roundNumber(area)} ${areaUnit}`;
|
|
887
894
|
textLines.push(areaLine);
|
|
888
895
|
}
|
|
889
896
|
|
|
890
897
|
if (mean) {
|
|
891
|
-
textLines.push(`Mean: ${mean
|
|
898
|
+
textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
|
|
892
899
|
}
|
|
893
900
|
|
|
894
901
|
if (max) {
|
|
895
|
-
textLines.push(`Max: ${max
|
|
902
|
+
textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
|
|
896
903
|
}
|
|
897
904
|
|
|
898
905
|
if (stdDev) {
|
|
899
|
-
textLines.push(`Std Dev: ${stdDev
|
|
906
|
+
textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
|
|
900
907
|
}
|
|
901
908
|
|
|
902
909
|
return textLines;
|
|
@@ -994,7 +1001,13 @@ class CircleROITool extends AnnotationTool {
|
|
|
994
1001
|
worldPos2
|
|
995
1002
|
);
|
|
996
1003
|
const isEmptyArea = worldWidth === 0 && worldHeight === 0;
|
|
997
|
-
const
|
|
1004
|
+
const scale = getCalibratedScale(image);
|
|
1005
|
+
const aspect = getCalibratedAspect(image);
|
|
1006
|
+
const area = Math.abs(
|
|
1007
|
+
Math.PI *
|
|
1008
|
+
(worldWidth / scale / 2) *
|
|
1009
|
+
(worldHeight / aspect / scale / 2)
|
|
1010
|
+
);
|
|
998
1011
|
|
|
999
1012
|
let count = 0;
|
|
1000
1013
|
let mean = 0;
|
|
@@ -1048,10 +1061,10 @@ class CircleROITool extends AnnotationTool {
|
|
|
1048
1061
|
max,
|
|
1049
1062
|
stdDev,
|
|
1050
1063
|
isEmptyArea,
|
|
1051
|
-
areaUnit:
|
|
1052
|
-
radius: worldWidth / 2,
|
|
1053
|
-
radiusUnit:
|
|
1054
|
-
perimeter: 2 * Math.PI * (worldWidth / 2),
|
|
1064
|
+
areaUnit: getCalibratedAreaUnits(null, image),
|
|
1065
|
+
radius: worldWidth / 2 / scale,
|
|
1066
|
+
radiusUnit: getCalibratedLengthUnits(null, image),
|
|
1067
|
+
perimeter: (2 * Math.PI * (worldWidth / 2)) / scale,
|
|
1055
1068
|
modalityUnit,
|
|
1056
1069
|
};
|
|
1057
1070
|
} else {
|
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
} from '@cornerstonejs/core';
|
|
10
10
|
import type { Types } from '@cornerstonejs/core';
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
getCalibratedAreaUnits,
|
|
14
|
+
getCalibratedScale,
|
|
15
|
+
} from '../../utilities/getCalibratedUnits';
|
|
16
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
12
17
|
import throttle from '../../utilities/throttle';
|
|
13
18
|
import {
|
|
14
19
|
addAnnotation,
|
|
@@ -994,20 +999,20 @@ class EllipticalROITool extends AnnotationTool {
|
|
|
994
999
|
if (area) {
|
|
995
1000
|
const areaLine = isEmptyArea
|
|
996
1001
|
? `Area: Oblique not supported`
|
|
997
|
-
: `Area: ${area
|
|
1002
|
+
: `Area: ${roundNumber(area)} ${areaUnit}`;
|
|
998
1003
|
textLines.push(areaLine);
|
|
999
1004
|
}
|
|
1000
1005
|
|
|
1001
1006
|
if (mean) {
|
|
1002
|
-
textLines.push(`Mean: ${mean
|
|
1007
|
+
textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
|
|
1003
1008
|
}
|
|
1004
1009
|
|
|
1005
1010
|
if (max) {
|
|
1006
|
-
textLines.push(`Max: ${max
|
|
1011
|
+
textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
|
|
1007
1012
|
}
|
|
1008
1013
|
|
|
1009
1014
|
if (stdDev) {
|
|
1010
|
-
textLines.push(`Std Dev: ${stdDev
|
|
1015
|
+
textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
|
|
1011
1016
|
}
|
|
1012
1017
|
|
|
1013
1018
|
return textLines;
|
|
@@ -1105,7 +1110,11 @@ class EllipticalROITool extends AnnotationTool {
|
|
|
1105
1110
|
worldPos2
|
|
1106
1111
|
);
|
|
1107
1112
|
const isEmptyArea = worldWidth === 0 && worldHeight === 0;
|
|
1108
|
-
const
|
|
1113
|
+
const scale = getCalibratedScale(image);
|
|
1114
|
+
const area =
|
|
1115
|
+
Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2)) /
|
|
1116
|
+
scale /
|
|
1117
|
+
scale;
|
|
1109
1118
|
|
|
1110
1119
|
let count = 0;
|
|
1111
1120
|
let mean = 0;
|
|
@@ -1159,7 +1168,7 @@ class EllipticalROITool extends AnnotationTool {
|
|
|
1159
1168
|
max,
|
|
1160
1169
|
stdDev,
|
|
1161
1170
|
isEmptyArea,
|
|
1162
|
-
areaUnit:
|
|
1171
|
+
areaUnit: getCalibratedAreaUnits(null, image),
|
|
1163
1172
|
modalityUnit,
|
|
1164
1173
|
};
|
|
1165
1174
|
} else {
|
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
} from '@cornerstonejs/core';
|
|
8
8
|
import type { Types } from '@cornerstonejs/core';
|
|
9
9
|
|
|
10
|
+
import {
|
|
11
|
+
getCalibratedLengthUnits,
|
|
12
|
+
getCalibratedScale,
|
|
13
|
+
} from '../../utilities/getCalibratedUnits';
|
|
14
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
10
15
|
import { AnnotationTool } from '../base';
|
|
11
16
|
import throttle from '../../utilities/throttle';
|
|
12
17
|
import {
|
|
@@ -43,7 +48,6 @@ import {
|
|
|
43
48
|
TextBoxHandle,
|
|
44
49
|
PublicToolProps,
|
|
45
50
|
ToolProps,
|
|
46
|
-
InteractionTypes,
|
|
47
51
|
SVGDrawingHelper,
|
|
48
52
|
} from '../../types';
|
|
49
53
|
import { LengthAnnotation } from '../../types/ToolSpecificAnnotationTypes';
|
|
@@ -776,7 +780,7 @@ class LengthTool extends AnnotationTool {
|
|
|
776
780
|
return;
|
|
777
781
|
}
|
|
778
782
|
|
|
779
|
-
const textLines = [`${length
|
|
783
|
+
const textLines = [`${roundNumber(length)} ${unit}`];
|
|
780
784
|
|
|
781
785
|
return textLines;
|
|
782
786
|
}
|
|
@@ -812,9 +816,10 @@ class LengthTool extends AnnotationTool {
|
|
|
812
816
|
continue;
|
|
813
817
|
}
|
|
814
818
|
|
|
815
|
-
const { imageData, dimensions
|
|
819
|
+
const { imageData, dimensions } = image;
|
|
820
|
+
const scale = getCalibratedScale(image);
|
|
816
821
|
|
|
817
|
-
const length = this._calculateLength(worldPos1, worldPos2);
|
|
822
|
+
const length = this._calculateLength(worldPos1, worldPos2) / scale;
|
|
818
823
|
|
|
819
824
|
const index1 = transformWorldToIndex(imageData, worldPos1);
|
|
820
825
|
const index2 = transformWorldToIndex(imageData, worldPos2);
|
|
@@ -830,7 +835,7 @@ class LengthTool extends AnnotationTool {
|
|
|
830
835
|
// todo: add insideVolume calculation, for removing tool if outside
|
|
831
836
|
cachedStats[targetId] = {
|
|
832
837
|
length,
|
|
833
|
-
unit:
|
|
838
|
+
unit: getCalibratedLengthUnits(null, image),
|
|
834
839
|
};
|
|
835
840
|
}
|
|
836
841
|
|
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
} from '@cornerstonejs/core';
|
|
10
10
|
import type { Types } from '@cornerstonejs/core';
|
|
11
11
|
import { vec3 } from 'gl-matrix';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getCalibratedAreaUnits,
|
|
15
|
+
getCalibratedScale,
|
|
16
|
+
} from '../../utilities/getCalibratedUnits';
|
|
17
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
12
18
|
import { Events } from '../../enums';
|
|
13
19
|
import { AnnotationTool } from '../base';
|
|
14
20
|
import {
|
|
@@ -737,9 +743,11 @@ class PlanarFreehandROITool extends AnnotationTool {
|
|
|
737
743
|
continue;
|
|
738
744
|
}
|
|
739
745
|
|
|
740
|
-
const { imageData, metadata
|
|
746
|
+
const { imageData, metadata } = image;
|
|
741
747
|
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
|
|
742
|
-
const
|
|
748
|
+
const scale = getCalibratedScale(image);
|
|
749
|
+
const area =
|
|
750
|
+
polyline.calculateAreaOfPoints(canvasCoordinates) / scale / scale;
|
|
743
751
|
|
|
744
752
|
const worldPosIndex = csUtils.transformWorldToIndex(imageData, points[0]);
|
|
745
753
|
worldPosIndex[0] = Math.floor(worldPosIndex[0]);
|
|
@@ -868,7 +876,7 @@ class PlanarFreehandROITool extends AnnotationTool {
|
|
|
868
876
|
mean,
|
|
869
877
|
max,
|
|
870
878
|
stdDev,
|
|
871
|
-
areaUnit:
|
|
879
|
+
areaUnit: getCalibratedAreaUnits(null, image),
|
|
872
880
|
modalityUnit,
|
|
873
881
|
};
|
|
874
882
|
}
|
|
@@ -939,20 +947,20 @@ class PlanarFreehandROITool extends AnnotationTool {
|
|
|
939
947
|
if (area) {
|
|
940
948
|
const areaLine = isEmptyArea
|
|
941
949
|
? `Area: Oblique not supported`
|
|
942
|
-
: `Area: ${area
|
|
950
|
+
: `Area: ${roundNumber(area)} ${areaUnit}`;
|
|
943
951
|
textLines.push(areaLine);
|
|
944
952
|
}
|
|
945
953
|
|
|
946
954
|
if (mean) {
|
|
947
|
-
textLines.push(`Mean: ${mean
|
|
955
|
+
textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
|
|
948
956
|
}
|
|
949
957
|
|
|
950
958
|
if (max) {
|
|
951
|
-
textLines.push(`Max: ${max
|
|
959
|
+
textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
|
|
952
960
|
}
|
|
953
961
|
|
|
954
962
|
if (stdDev) {
|
|
955
|
-
textLines.push(`Std Dev: ${stdDev
|
|
963
|
+
textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
|
|
956
964
|
}
|
|
957
965
|
|
|
958
966
|
return textLines;
|
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
} from '@cornerstonejs/core';
|
|
10
10
|
import type { Types } from '@cornerstonejs/core';
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
getCalibratedAreaUnits,
|
|
14
|
+
getCalibratedScale,
|
|
15
|
+
} from '../../utilities/getCalibratedUnits';
|
|
16
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
12
17
|
import throttle from '../../utilities/throttle';
|
|
13
18
|
import {
|
|
14
19
|
addAnnotation,
|
|
@@ -859,10 +864,10 @@ class RectangleROITool extends AnnotationTool {
|
|
|
859
864
|
|
|
860
865
|
const textLines: string[] = [];
|
|
861
866
|
|
|
862
|
-
textLines.push(`Area: ${area
|
|
863
|
-
textLines.push(`Mean: ${mean
|
|
864
|
-
textLines.push(`Max: ${max
|
|
865
|
-
textLines.push(`Std Dev: ${stdDev
|
|
867
|
+
textLines.push(`Area: ${roundNumber(area)} ${areaUnit}`);
|
|
868
|
+
textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
|
|
869
|
+
textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
|
|
870
|
+
textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
|
|
866
871
|
|
|
867
872
|
return textLines;
|
|
868
873
|
};
|
|
@@ -907,7 +912,7 @@ class RectangleROITool extends AnnotationTool {
|
|
|
907
912
|
continue;
|
|
908
913
|
}
|
|
909
914
|
|
|
910
|
-
const { dimensions, imageData, metadata
|
|
915
|
+
const { dimensions, imageData, metadata } = image;
|
|
911
916
|
const scalarData =
|
|
912
917
|
'getScalarData' in image ? image.getScalarData() : image.scalarData;
|
|
913
918
|
|
|
@@ -946,8 +951,9 @@ class RectangleROITool extends AnnotationTool {
|
|
|
946
951
|
worldPos1,
|
|
947
952
|
worldPos2
|
|
948
953
|
);
|
|
954
|
+
const scale = getCalibratedScale(image);
|
|
949
955
|
|
|
950
|
-
const area = Math.abs(worldWidth * worldHeight);
|
|
956
|
+
const area = Math.abs(worldWidth * worldHeight) / (scale * scale);
|
|
951
957
|
|
|
952
958
|
let count = 0;
|
|
953
959
|
let mean = 0;
|
|
@@ -1004,7 +1010,7 @@ class RectangleROITool extends AnnotationTool {
|
|
|
1004
1010
|
mean,
|
|
1005
1011
|
stdDev,
|
|
1006
1012
|
max,
|
|
1007
|
-
areaUnit:
|
|
1013
|
+
areaUnit: getCalibratedAreaUnits(null, image),
|
|
1008
1014
|
modalityUnit,
|
|
1009
1015
|
};
|
|
1010
1016
|
} else {
|
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
} from '@cornerstonejs/core';
|
|
8
8
|
import type { Types } from '@cornerstonejs/core';
|
|
9
9
|
|
|
10
|
-
import { vec4 } from 'gl-matrix';
|
|
11
|
-
|
|
12
10
|
import BaseTool from './BaseTool';
|
|
13
11
|
import { getAnnotationManager } from '../../stateManagement/annotation/annotationState';
|
|
14
12
|
import { Annotation, Annotations, SVGDrawingHelper } from '../../types';
|
|
@@ -30,6 +28,7 @@ import { StyleSpecifier } from '../../types/AnnotationStyle';
|
|
|
30
28
|
*/
|
|
31
29
|
abstract class AnnotationDisplayTool extends BaseTool {
|
|
32
30
|
static toolName;
|
|
31
|
+
|
|
33
32
|
// ===================================================================
|
|
34
33
|
// Abstract Methods - Must be implemented.
|
|
35
34
|
// ===================================================================
|
|
@@ -83,31 +82,16 @@ abstract class AnnotationDisplayTool extends BaseTool {
|
|
|
83
82
|
public onImageSpacingCalibrated = (
|
|
84
83
|
evt: Types.EventTypes.ImageSpacingCalibratedEvent
|
|
85
84
|
) => {
|
|
86
|
-
const {
|
|
87
|
-
element,
|
|
88
|
-
rowScale,
|
|
89
|
-
columnScale,
|
|
90
|
-
imageId,
|
|
91
|
-
imageData: calibratedImageData,
|
|
92
|
-
worldToIndex: nonCalibratedWorldToIndex,
|
|
93
|
-
} = evt.detail;
|
|
94
|
-
|
|
95
|
-
const { viewport } = getEnabledElement(element);
|
|
96
|
-
|
|
97
|
-
if (viewport instanceof VolumeViewport) {
|
|
98
|
-
throw new Error('Cannot calibrate a volume viewport');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const calibratedIndexToWorld = calibratedImageData.getIndexToWorld();
|
|
85
|
+
const { element, imageId } = evt.detail;
|
|
102
86
|
|
|
103
87
|
const imageURI = utilities.imageIdToURI(imageId);
|
|
104
|
-
const
|
|
105
|
-
const framesOfReference =
|
|
88
|
+
const annotationManager = getAnnotationManager();
|
|
89
|
+
const framesOfReference = annotationManager.getFramesOfReference();
|
|
106
90
|
|
|
107
91
|
// For each frame Of Reference
|
|
108
92
|
framesOfReference.forEach((frameOfReference) => {
|
|
109
93
|
const frameOfReferenceSpecificAnnotations =
|
|
110
|
-
|
|
94
|
+
annotationManager.getAnnotations(frameOfReference);
|
|
111
95
|
|
|
112
96
|
const toolSpecificAnnotations =
|
|
113
97
|
frameOfReferenceSpecificAnnotations[this.getToolName()];
|
|
@@ -128,44 +112,8 @@ abstract class AnnotationDisplayTool extends BaseTool {
|
|
|
128
112
|
// we can update the cachedStats and also rendering
|
|
129
113
|
annotation.invalidated = true;
|
|
130
114
|
annotation.data.cachedStats = {};
|
|
131
|
-
|
|
132
|
-
// Update annotation points to the new calibrated points. Basically,
|
|
133
|
-
// using the worldToIndex function we get the index on the non-calibrated
|
|
134
|
-
// image and then using the calibratedIndexToWorld function we get the
|
|
135
|
-
// corresponding point on the calibrated image world.
|
|
136
|
-
annotation.data.handles.points = annotation.data.handles.points.map(
|
|
137
|
-
(point) => {
|
|
138
|
-
const p = vec4.fromValues(...(point as Types.Point3), 1);
|
|
139
|
-
const pCalibrated = vec4.fromValues(0, 0, 0, 1);
|
|
140
|
-
const nonCalibratedIndexVec4 = vec4.create();
|
|
141
|
-
vec4.transformMat4(
|
|
142
|
-
nonCalibratedIndexVec4,
|
|
143
|
-
p,
|
|
144
|
-
nonCalibratedWorldToIndex
|
|
145
|
-
);
|
|
146
|
-
const calibratedIndex = [
|
|
147
|
-
columnScale * nonCalibratedIndexVec4[0],
|
|
148
|
-
rowScale * nonCalibratedIndexVec4[1],
|
|
149
|
-
nonCalibratedIndexVec4[2],
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
vec4.transformMat4(
|
|
153
|
-
pCalibrated,
|
|
154
|
-
vec4.fromValues(
|
|
155
|
-
calibratedIndex[0],
|
|
156
|
-
calibratedIndex[1],
|
|
157
|
-
calibratedIndex[2],
|
|
158
|
-
1
|
|
159
|
-
),
|
|
160
|
-
calibratedIndexToWorld
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
return pCalibrated.slice(0, 3) as Types.Point3;
|
|
164
|
-
}
|
|
165
|
-
);
|
|
166
115
|
}
|
|
167
116
|
});
|
|
168
|
-
|
|
169
117
|
triggerAnnotationRender(element);
|
|
170
118
|
});
|
|
171
119
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { utilities } from '@cornerstonejs/core';
|
|
1
|
+
import { utilities, Enums } from '@cornerstonejs/core';
|
|
2
2
|
import type { Types } from '@cornerstonejs/core';
|
|
3
3
|
|
|
4
4
|
const { calibratedPixelSpacingMetadataProvider } = utilities;
|
|
@@ -9,25 +9,22 @@ const { calibratedPixelSpacingMetadataProvider } = utilities;
|
|
|
9
9
|
* their reference imageIds. Finally, it triggers a re-render for invalidated annotations.
|
|
10
10
|
* @param imageId - ImageId for the calibrated image
|
|
11
11
|
* @param rowPixelSpacing - Spacing in row direction
|
|
12
|
-
* @param
|
|
13
|
-
* @param renderingEngine - Cornerstone RenderingEngine instance
|
|
12
|
+
* @param calibrationOrScale - either the calibration object or a scale value
|
|
14
13
|
*/
|
|
15
14
|
export default function calibrateImageSpacing(
|
|
16
15
|
imageId: string,
|
|
17
16
|
renderingEngine: Types.IRenderingEngine,
|
|
18
|
-
|
|
19
|
-
columnPixelSpacing: number
|
|
17
|
+
calibrationOrScale: Types.IImageCalibration | number
|
|
20
18
|
): void {
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
// Handle simple parameter version
|
|
20
|
+
if (typeof calibrationOrScale === 'number') {
|
|
21
|
+
calibrationOrScale = {
|
|
22
|
+
type: Enums.CalibrationTypes.USER,
|
|
23
|
+
scale: calibrationOrScale,
|
|
24
|
+
};
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
calibratedPixelSpacingMetadataProvider.add(imageId,
|
|
28
|
-
rowPixelSpacing,
|
|
29
|
-
columnPixelSpacing,
|
|
30
|
-
});
|
|
26
|
+
// 1. Add the calibratedPixelSpacing metadata to the metadata
|
|
27
|
+
calibratedPixelSpacingMetadataProvider.add(imageId, calibrationOrScale);
|
|
31
28
|
|
|
32
29
|
// 2. Update the actor for stackViewports
|
|
33
30
|
const viewports = renderingEngine.getStackViewports();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Enums } from '@cornerstonejs/core';
|
|
2
|
+
|
|
3
|
+
const { CalibrationTypes } = Enums;
|
|
4
|
+
const PIXEL_UNITS = 'px';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extracts the length units and the type of calibration for those units
|
|
8
|
+
* into the response. The length units will typically be either mm or px
|
|
9
|
+
* while the calibration type can be any of a number of different calibraiton types.
|
|
10
|
+
*
|
|
11
|
+
* Volumetric images have no calibration type, so are just the raw mm.
|
|
12
|
+
*
|
|
13
|
+
* TODO: Handle region calibration
|
|
14
|
+
*
|
|
15
|
+
* @param handles - used to detect if the spacing information is different
|
|
16
|
+
* between various points (eg angled ERMF or US Region).
|
|
17
|
+
* Currently unused, but needed for correct US Region handling
|
|
18
|
+
* @param image - to extract the calibration from
|
|
19
|
+
* image.calibration - calibration value to extract units form
|
|
20
|
+
* @returns String containing the units and type of calibration
|
|
21
|
+
*/
|
|
22
|
+
const getCalibratedLengthUnits = (handles, image): string => {
|
|
23
|
+
const { calibration, hasPixelSpacing } = image;
|
|
24
|
+
// Anachronistic - moving to using calibration consistently, but not completed yet
|
|
25
|
+
const units = hasPixelSpacing ? 'mm' : PIXEL_UNITS;
|
|
26
|
+
if (!calibration || !calibration.type) return units;
|
|
27
|
+
if (calibration.type === CalibrationTypes.UNCALIBRATED) return PIXEL_UNITS;
|
|
28
|
+
// TODO - handle US regions properly
|
|
29
|
+
if (calibration.SequenceOfUltrasoundRegions) return 'US Region';
|
|
30
|
+
return `${units} ${calibration.type}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const SQUARE = '\xb2';
|
|
34
|
+
/**
|
|
35
|
+
* Extracts the area units, including the squared sign plus calibration type.
|
|
36
|
+
*/
|
|
37
|
+
const getCalibratedAreaUnits = (handles, image): string => {
|
|
38
|
+
const { calibration, hasPixelSpacing } = image;
|
|
39
|
+
const units = (hasPixelSpacing ? 'mm' : PIXEL_UNITS) + SQUARE;
|
|
40
|
+
if (!calibration || !calibration.type) return units;
|
|
41
|
+
if (calibration.SequenceOfUltrasoundRegions) return 'US Region';
|
|
42
|
+
return `${units} ${calibration.type}`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Gets the scale divisor for converting from internal spacing to
|
|
47
|
+
* image spacing for calibrated images.
|
|
48
|
+
*/
|
|
49
|
+
const getCalibratedScale = (image) => image.calibration?.scale || 1;
|
|
50
|
+
|
|
51
|
+
/** Gets the aspect ratio of the screen display relative to the image
|
|
52
|
+
* display in order to square up measurement values.
|
|
53
|
+
* That is, suppose the spacing on the image is 1, 0.5 (x,y spacing)
|
|
54
|
+
* This is displayed at 1, 1 spacing on screen, then the
|
|
55
|
+
* aspect value will be 1/0.5 = 2
|
|
56
|
+
*/
|
|
57
|
+
const getCalibratedAspect = (image) => image.calibration?.aspect || 1;
|
|
58
|
+
|
|
59
|
+
export default getCalibratedLengthUnits;
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
getCalibratedAreaUnits,
|
|
63
|
+
getCalibratedLengthUnits,
|
|
64
|
+
getCalibratedScale,
|
|
65
|
+
getCalibratedAspect,
|
|
66
|
+
};
|
package/src/utilities/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import jumpToSlice from './viewport/jumpToSlice';
|
|
|
16
16
|
import pointInShapeCallback from './pointInShapeCallback';
|
|
17
17
|
import pointInSurroundingSphereCallback from './pointInSurroundingSphereCallback';
|
|
18
18
|
import scroll from './scroll';
|
|
19
|
+
import roundNumber from './roundNumber';
|
|
19
20
|
|
|
20
21
|
// name spaces
|
|
21
22
|
import * as segmentation from './segmentation';
|
|
@@ -65,4 +66,5 @@ export {
|
|
|
65
66
|
planarFreehandROITool,
|
|
66
67
|
stackPrefetch,
|
|
67
68
|
scroll,
|
|
69
|
+
roundNumber,
|
|
68
70
|
};
|