@cornerstonejs/tools 1.4.6 → 1.6.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.
Files changed (71) hide show
  1. package/dist/cjs/tools/annotation/AngleTool.js +9 -4
  2. package/dist/cjs/tools/annotation/AngleTool.js.map +1 -1
  3. package/dist/cjs/tools/annotation/BidirectionalTool.js +9 -6
  4. package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
  5. package/dist/cjs/tools/annotation/CircleROITool.js +16 -10
  6. package/dist/cjs/tools/annotation/CircleROITool.js.map +1 -1
  7. package/dist/cjs/tools/annotation/EllipticalROITool.js +11 -6
  8. package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
  9. package/dist/cjs/tools/annotation/LengthTool.js +7 -4
  10. package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
  11. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +10 -7
  12. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  13. package/dist/cjs/tools/annotation/RectangleROITool.js +10 -7
  14. package/dist/cjs/tools/annotation/RectangleROITool.js.map +1 -1
  15. package/dist/cjs/tools/base/AnnotationDisplayTool.js +4 -23
  16. package/dist/cjs/tools/base/AnnotationDisplayTool.js.map +1 -1
  17. package/dist/cjs/utilities/calibrateImageSpacing.d.ts +1 -1
  18. package/dist/cjs/utilities/calibrateImageSpacing.js +7 -7
  19. package/dist/cjs/utilities/calibrateImageSpacing.js.map +1 -1
  20. package/dist/cjs/utilities/getCalibratedUnits.d.ts +6 -0
  21. package/dist/cjs/utilities/getCalibratedUnits.js +35 -0
  22. package/dist/cjs/utilities/getCalibratedUnits.js.map +1 -0
  23. package/dist/cjs/utilities/index.d.ts +2 -1
  24. package/dist/cjs/utilities/index.js +3 -1
  25. package/dist/cjs/utilities/index.js.map +1 -1
  26. package/dist/cjs/utilities/roundNumber.d.ts +2 -0
  27. package/dist/cjs/utilities/roundNumber.js +25 -0
  28. package/dist/cjs/utilities/roundNumber.js.map +1 -0
  29. package/dist/esm/tools/annotation/AngleTool.js +9 -4
  30. package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
  31. package/dist/esm/tools/annotation/BidirectionalTool.js +9 -6
  32. package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
  33. package/dist/esm/tools/annotation/CircleROITool.js +16 -10
  34. package/dist/esm/tools/annotation/CircleROITool.js.map +1 -1
  35. package/dist/esm/tools/annotation/EllipticalROITool.js +11 -6
  36. package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
  37. package/dist/esm/tools/annotation/LengthTool.js +7 -4
  38. package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
  39. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +10 -7
  40. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  41. package/dist/esm/tools/annotation/RectangleROITool.js +10 -7
  42. package/dist/esm/tools/annotation/RectangleROITool.js.map +1 -1
  43. package/dist/esm/tools/base/AnnotationDisplayTool.js +5 -24
  44. package/dist/esm/tools/base/AnnotationDisplayTool.js.map +1 -1
  45. package/dist/esm/utilities/calibrateImageSpacing.d.ts +1 -1
  46. package/dist/esm/utilities/calibrateImageSpacing.js +8 -8
  47. package/dist/esm/utilities/calibrateImageSpacing.js.map +1 -1
  48. package/dist/esm/utilities/getCalibratedUnits.d.ts +6 -0
  49. package/dist/esm/utilities/getCalibratedUnits.js +29 -0
  50. package/dist/esm/utilities/getCalibratedUnits.js.map +1 -0
  51. package/dist/esm/utilities/index.d.ts +2 -1
  52. package/dist/esm/utilities/index.js +2 -1
  53. package/dist/esm/utilities/index.js.map +1 -1
  54. package/dist/esm/utilities/roundNumber.d.ts +2 -0
  55. package/dist/esm/utilities/roundNumber.js +23 -0
  56. package/dist/esm/utilities/roundNumber.js.map +1 -0
  57. package/dist/umd/index.js +1 -1
  58. package/dist/umd/index.js.map +1 -1
  59. package/package.json +3 -3
  60. package/src/tools/annotation/AngleTool.ts +14 -9
  61. package/src/tools/annotation/BidirectionalTool.ts +13 -8
  62. package/src/tools/annotation/CircleROITool.ts +23 -10
  63. package/src/tools/annotation/EllipticalROITool.ts +15 -6
  64. package/src/tools/annotation/LengthTool.ts +10 -5
  65. package/src/tools/annotation/PlanarFreehandROITool.ts +15 -7
  66. package/src/tools/annotation/RectangleROITool.ts +13 -7
  67. package/src/tools/base/AnnotationDisplayTool.ts +5 -57
  68. package/src/utilities/calibrateImageSpacing.ts +11 -14
  69. package/src/utilities/getCalibratedUnits.ts +66 -0
  70. package/src/utilities/index.ts +2 -0
  71. 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.4.6",
3
+ "version": "1.6.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.4.6",
30
+ "@cornerstonejs/core": "^1.6.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": "611a684e4c3674d12cc66b749cf19d0f1b5698da"
53
+ "gitHead": "cfde297ad3a3ef5f1976751654c847ea63610749"
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 (distanceToPoint <= proximity || distanceToPoint2 <= proximity) {
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.toFixed(2)} ${String.fromCharCode(176)}`];
791
+ const textLines = [`${roundNumber(angle)} ${String.fromCharCode(176)}`];
787
792
 
788
793
  return textLines;
789
794
  }
@@ -1,4 +1,4 @@
1
- import { vec2, vec3, mat2, mat3, mat2d } from 'gl-matrix';
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.toFixed(2)} ${unit}`,
1256
- `W: ${width.toFixed(2)} ${unit}`,
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, hasPixelSpacing } = image;
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: hasPixelSpacing ? 'mm' : 'px',
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.toFixed(2)} ${radiusUnit}`;
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.toFixed(2)} ${areaUnit}\xb2`;
893
+ : `Area: ${roundNumber(area)} ${areaUnit}`;
887
894
  textLines.push(areaLine);
888
895
  }
889
896
 
890
897
  if (mean) {
891
- textLines.push(`Mean: ${mean.toFixed(2)} ${modalityUnit}`);
898
+ textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
892
899
  }
893
900
 
894
901
  if (max) {
895
- textLines.push(`Max: ${max.toFixed(2)} ${modalityUnit}`);
902
+ textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
896
903
  }
897
904
 
898
905
  if (stdDev) {
899
- textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${modalityUnit}`);
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 area = Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2));
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: hasPixelSpacing ? 'mm' : 'px',
1052
- radius: worldWidth / 2,
1053
- radiusUnit: hasPixelSpacing ? 'mm' : 'px',
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.toFixed(2)} ${areaUnit}\xb2`;
1002
+ : `Area: ${roundNumber(area)} ${areaUnit}`;
998
1003
  textLines.push(areaLine);
999
1004
  }
1000
1005
 
1001
1006
  if (mean) {
1002
- textLines.push(`Mean: ${mean.toFixed(2)} ${modalityUnit}`);
1007
+ textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
1003
1008
  }
1004
1009
 
1005
1010
  if (max) {
1006
- textLines.push(`Max: ${max.toFixed(2)} ${modalityUnit}`);
1011
+ textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
1007
1012
  }
1008
1013
 
1009
1014
  if (stdDev) {
1010
- textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${modalityUnit}`);
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 area = Math.abs(Math.PI * (worldWidth / 2) * (worldHeight / 2));
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: hasPixelSpacing ? 'mm' : 'px',
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.toFixed(2)} ${unit}`];
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, hasPixelSpacing } = image;
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: hasPixelSpacing ? 'mm' : 'px',
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, hasPixelSpacing } = image;
746
+ const { imageData, metadata } = image;
741
747
  const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
742
- const area = polyline.calculateAreaOfPoints(canvasCoordinates);
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: hasPixelSpacing ? 'mm' : 'px',
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.toFixed(2)} ${areaUnit}\xb2`;
950
+ : `Area: ${roundNumber(area)} ${areaUnit}`;
943
951
  textLines.push(areaLine);
944
952
  }
945
953
 
946
954
  if (mean) {
947
- textLines.push(`Mean: ${mean.toFixed(2)} ${modalityUnit}`);
955
+ textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
948
956
  }
949
957
 
950
958
  if (max) {
951
- textLines.push(`Max: ${max.toFixed(2)} ${modalityUnit}`);
959
+ textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
952
960
  }
953
961
 
954
962
  if (stdDev) {
955
- textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${modalityUnit}`);
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.toFixed(2)} ${areaUnit}\xb2`);
863
- textLines.push(`Mean: ${mean.toFixed(2)} ${modalityUnit}`);
864
- textLines.push(`Max: ${max.toFixed(2)} ${modalityUnit}`);
865
- textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${modalityUnit}`);
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, hasPixelSpacing } = image;
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: hasPixelSpacing ? 'mm' : 'px',
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 stateManager = getAnnotationManager();
105
- const framesOfReference = stateManager.getFramesOfReference();
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
- stateManager.getAnnotations(frameOfReference);
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 columnPixelSpacing - Spacing in column direction
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
- rowPixelSpacing: number,
19
- columnPixelSpacing: number
17
+ calibrationOrScale: Types.IImageCalibration | number
20
18
  ): void {
21
- // 1. Add the calibratedPixelSpacing metadata to the metadata provider
22
- // If no column spacing provided, assume square pixels
23
- if (!columnPixelSpacing) {
24
- columnPixelSpacing = rowPixelSpacing;
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
+ };
@@ -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
  };