@cornerstonejs/tools 1.12.1 → 1.13.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 (136) hide show
  1. package/dist/cjs/store/ToolGroupManager/ToolGroup.d.ts +3 -3
  2. package/dist/cjs/store/ToolGroupManager/ToolGroup.js.map +1 -1
  3. package/dist/cjs/tools/annotation/AngleTool.d.ts +0 -1
  4. package/dist/cjs/tools/annotation/AngleTool.js +11 -10
  5. package/dist/cjs/tools/annotation/AngleTool.js.map +1 -1
  6. package/dist/cjs/tools/annotation/BidirectionalTool.d.ts +0 -1
  7. package/dist/cjs/tools/annotation/BidirectionalTool.js +14 -13
  8. package/dist/cjs/tools/annotation/BidirectionalTool.js.map +1 -1
  9. package/dist/cjs/tools/annotation/CircleROITool.d.ts +0 -1
  10. package/dist/cjs/tools/annotation/CircleROITool.js +39 -51
  11. package/dist/cjs/tools/annotation/CircleROITool.js.map +1 -1
  12. package/dist/cjs/tools/annotation/CobbAngleTool.d.ts +0 -1
  13. package/dist/cjs/tools/annotation/CobbAngleTool.js +11 -10
  14. package/dist/cjs/tools/annotation/CobbAngleTool.js.map +1 -1
  15. package/dist/cjs/tools/annotation/DragProbeTool.js +13 -1
  16. package/dist/cjs/tools/annotation/DragProbeTool.js.map +1 -1
  17. package/dist/cjs/tools/annotation/EllipticalROITool.d.ts +0 -1
  18. package/dist/cjs/tools/annotation/EllipticalROITool.js +33 -45
  19. package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
  20. package/dist/cjs/tools/annotation/LengthTool.d.ts +0 -1
  21. package/dist/cjs/tools/annotation/LengthTool.js +11 -10
  22. package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
  23. package/dist/cjs/tools/annotation/PlanarFreehandROITool.d.ts +0 -1
  24. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +34 -42
  25. package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  26. package/dist/cjs/tools/annotation/ProbeTool.d.ts +0 -1
  27. package/dist/cjs/tools/annotation/ProbeTool.js +13 -12
  28. package/dist/cjs/tools/annotation/ProbeTool.js.map +1 -1
  29. package/dist/cjs/tools/annotation/RectangleROITool.d.ts +0 -1
  30. package/dist/cjs/tools/annotation/RectangleROITool.js +31 -47
  31. package/dist/cjs/tools/annotation/RectangleROITool.js.map +1 -1
  32. package/dist/cjs/tools/base/AnnotationTool.d.ts +2 -1
  33. package/dist/cjs/tools/base/AnnotationTool.js +10 -2
  34. package/dist/cjs/tools/base/AnnotationTool.js.map +1 -1
  35. package/dist/cjs/types/CalculatorTypes.d.ts +6 -0
  36. package/dist/cjs/types/CalculatorTypes.js +3 -0
  37. package/dist/cjs/types/CalculatorTypes.js.map +1 -0
  38. package/dist/cjs/types/IToolGroup.d.ts +3 -2
  39. package/dist/cjs/types/ToolProps.d.ts +5 -1
  40. package/dist/cjs/types/index.d.ts +3 -2
  41. package/dist/cjs/utilities/math/basic/BasicStatsCalculator.d.ts +14 -0
  42. package/dist/cjs/utilities/math/basic/BasicStatsCalculator.js +44 -0
  43. package/dist/cjs/utilities/math/basic/BasicStatsCalculator.js.map +1 -0
  44. package/dist/cjs/utilities/math/basic/Calculator.d.ts +8 -0
  45. package/dist/cjs/utilities/math/basic/Calculator.js +6 -0
  46. package/dist/cjs/utilities/math/basic/Calculator.js.map +1 -0
  47. package/dist/cjs/utilities/math/basic/index.d.ts +3 -0
  48. package/dist/cjs/utilities/math/basic/index.js +11 -0
  49. package/dist/cjs/utilities/math/basic/index.js.map +1 -0
  50. package/dist/cjs/utilities/math/index.d.ts +2 -1
  51. package/dist/cjs/utilities/math/index.js +3 -1
  52. package/dist/cjs/utilities/math/index.js.map +1 -1
  53. package/dist/cjs/utilities/math/vec2/liangBarksyClip.d.ts +1 -1
  54. package/dist/cjs/utilities/pointInShapeCallback.d.ts +7 -1
  55. package/dist/cjs/utilities/pointInShapeCallback.js +6 -1
  56. package/dist/cjs/utilities/pointInShapeCallback.js.map +1 -1
  57. package/dist/esm/store/ToolGroupManager/ToolGroup.d.ts +3 -3
  58. package/dist/esm/store/ToolGroupManager/ToolGroup.js.map +1 -1
  59. package/dist/esm/tools/annotation/AngleTool.d.ts +0 -1
  60. package/dist/esm/tools/annotation/AngleTool.js +11 -10
  61. package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
  62. package/dist/esm/tools/annotation/BidirectionalTool.d.ts +0 -1
  63. package/dist/esm/tools/annotation/BidirectionalTool.js +14 -13
  64. package/dist/esm/tools/annotation/BidirectionalTool.js.map +1 -1
  65. package/dist/esm/tools/annotation/CircleROITool.d.ts +0 -1
  66. package/dist/esm/tools/annotation/CircleROITool.js +38 -51
  67. package/dist/esm/tools/annotation/CircleROITool.js.map +1 -1
  68. package/dist/esm/tools/annotation/CobbAngleTool.d.ts +0 -1
  69. package/dist/esm/tools/annotation/CobbAngleTool.js +11 -10
  70. package/dist/esm/tools/annotation/CobbAngleTool.js.map +1 -1
  71. package/dist/esm/tools/annotation/DragProbeTool.js +13 -1
  72. package/dist/esm/tools/annotation/DragProbeTool.js.map +1 -1
  73. package/dist/esm/tools/annotation/EllipticalROITool.d.ts +0 -1
  74. package/dist/esm/tools/annotation/EllipticalROITool.js +32 -45
  75. package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
  76. package/dist/esm/tools/annotation/LengthTool.d.ts +0 -1
  77. package/dist/esm/tools/annotation/LengthTool.js +11 -10
  78. package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
  79. package/dist/esm/tools/annotation/PlanarFreehandROITool.d.ts +0 -1
  80. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +33 -42
  81. package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
  82. package/dist/esm/tools/annotation/ProbeTool.d.ts +0 -1
  83. package/dist/esm/tools/annotation/ProbeTool.js +13 -12
  84. package/dist/esm/tools/annotation/ProbeTool.js.map +1 -1
  85. package/dist/esm/tools/annotation/RectangleROITool.d.ts +0 -1
  86. package/dist/esm/tools/annotation/RectangleROITool.js +30 -47
  87. package/dist/esm/tools/annotation/RectangleROITool.js.map +1 -1
  88. package/dist/esm/tools/base/AnnotationTool.d.ts +2 -1
  89. package/dist/esm/tools/base/AnnotationTool.js +9 -2
  90. package/dist/esm/tools/base/AnnotationTool.js.map +1 -1
  91. package/dist/esm/types/CalculatorTypes.d.ts +6 -0
  92. package/dist/esm/types/CalculatorTypes.js +2 -0
  93. package/dist/esm/types/CalculatorTypes.js.map +1 -0
  94. package/dist/esm/types/IToolGroup.d.ts +3 -2
  95. package/dist/esm/types/ToolProps.d.ts +5 -1
  96. package/dist/esm/types/index.d.ts +3 -2
  97. package/dist/esm/utilities/math/basic/BasicStatsCalculator.d.ts +14 -0
  98. package/dist/esm/utilities/math/basic/BasicStatsCalculator.js +36 -0
  99. package/dist/esm/utilities/math/basic/BasicStatsCalculator.js.map +1 -0
  100. package/dist/esm/utilities/math/basic/Calculator.d.ts +8 -0
  101. package/dist/esm/utilities/math/basic/Calculator.js +4 -0
  102. package/dist/esm/utilities/math/basic/Calculator.js.map +1 -0
  103. package/dist/esm/utilities/math/basic/index.d.ts +3 -0
  104. package/dist/esm/utilities/math/basic/index.js +4 -0
  105. package/dist/esm/utilities/math/basic/index.js.map +1 -0
  106. package/dist/esm/utilities/math/index.d.ts +2 -1
  107. package/dist/esm/utilities/math/index.js +2 -1
  108. package/dist/esm/utilities/math/index.js.map +1 -1
  109. package/dist/esm/utilities/math/vec2/liangBarksyClip.d.ts +1 -1
  110. package/dist/esm/utilities/pointInShapeCallback.d.ts +7 -1
  111. package/dist/esm/utilities/pointInShapeCallback.js +6 -1
  112. package/dist/esm/utilities/pointInShapeCallback.js.map +1 -1
  113. package/dist/umd/index.js +1 -1
  114. package/dist/umd/index.js.map +1 -1
  115. package/package.json +3 -3
  116. package/src/store/ToolGroupManager/ToolGroup.ts +4 -3
  117. package/src/tools/annotation/AngleTool.ts +15 -15
  118. package/src/tools/annotation/BidirectionalTool.ts +21 -22
  119. package/src/tools/annotation/CircleROITool.ts +64 -87
  120. package/src/tools/annotation/CobbAngleTool.ts +15 -15
  121. package/src/tools/annotation/DragProbeTool.ts +19 -1
  122. package/src/tools/annotation/EllipticalROITool.ts +47 -70
  123. package/src/tools/annotation/LengthTool.ts +16 -16
  124. package/src/tools/annotation/PlanarFreehandROITool.ts +36 -49
  125. package/src/tools/annotation/ProbeTool.ts +20 -18
  126. package/src/tools/annotation/RectangleROITool.ts +51 -74
  127. package/src/tools/base/AnnotationTool.ts +16 -1
  128. package/src/types/CalculatorTypes.ts +7 -0
  129. package/src/types/IToolGroup.ts +6 -3
  130. package/src/types/ToolProps.ts +7 -1
  131. package/src/types/index.ts +9 -1
  132. package/src/utilities/math/basic/BasicStatsCalculator.ts +60 -0
  133. package/src/utilities/math/basic/Calculator.ts +8 -0
  134. package/src/utilities/math/basic/index.ts +4 -0
  135. package/src/utilities/math/index.ts +10 -1
  136. package/src/utilities/pointInShapeCallback.ts +15 -3
@@ -113,6 +113,7 @@ class LengthTool extends AnnotationTool {
113
113
  supportedInteractionTypes: ['Mouse', 'Touch'],
114
114
  configuration: {
115
115
  preventHandleOutsideImage: false,
116
+ getTextLines: defaultGetTextLines,
116
117
  },
117
118
  }
118
119
  ) {
@@ -731,7 +732,7 @@ class LengthTool extends AnnotationTool {
731
732
  return renderStatus;
732
733
  }
733
734
 
734
- const textLines = this._getTextLines(data, targetId);
735
+ const textLines = this.configuration.getTextLines(data, targetId);
735
736
 
736
737
  // Need to update to sync with annotation while unlinked/not moved
737
738
  if (!data.handles.textBox.hasMoved) {
@@ -770,21 +771,6 @@ class LengthTool extends AnnotationTool {
770
771
  return renderStatus;
771
772
  };
772
773
 
773
- // text line for the current active length annotation
774
- _getTextLines(data, targetId) {
775
- const cachedVolumeStats = data.cachedStats[targetId];
776
- const { length, unit } = cachedVolumeStats;
777
-
778
- // Can be null on load
779
- if (length === undefined || length === null || isNaN(length)) {
780
- return;
781
- }
782
-
783
- const textLines = [`${roundNumber(length)} ${unit}`];
784
-
785
- return textLines;
786
- }
787
-
788
774
  _calculateLength(pos1, pos2) {
789
775
  const dx = pos1[0] - pos2[0];
790
776
  const dy = pos1[1] - pos2[1];
@@ -862,5 +848,19 @@ class LengthTool extends AnnotationTool {
862
848
  }
863
849
  }
864
850
 
851
+ function defaultGetTextLines(data, targetId): string[] {
852
+ const cachedVolumeStats = data.cachedStats[targetId];
853
+ const { length, unit } = cachedVolumeStats;
854
+
855
+ // Can be null on load
856
+ if (length === undefined || length === null || isNaN(length)) {
857
+ return;
858
+ }
859
+
860
+ const textLines = [`${roundNumber(length)} ${unit}`];
861
+
862
+ return textLines;
863
+ }
864
+
865
865
  LengthTool.toolName = 'Length';
866
866
  export default LengthTool;
@@ -59,6 +59,7 @@ import {
59
59
  ModalityUnitOptions,
60
60
  getModalityUnit,
61
61
  } from '../../utilities/getModalityUnit';
62
+ import { BasicStatsCalculator } from '../../utilities/math/basic';
62
63
 
63
64
  const { pointCanProjectOnLine } = polyline;
64
65
  const { EPSILON } = CONSTANTS;
@@ -124,6 +125,7 @@ const PARALLEL_THRESHOLD = 1 - EPSILON;
124
125
  *
125
126
  * Read more in the Docs section of the website.
126
127
  */
128
+
127
129
  class PlanarFreehandROITool extends AnnotationTool {
128
130
  static toolName;
129
131
 
@@ -215,6 +217,8 @@ class PlanarFreehandROITool extends AnnotationTool {
215
217
  knotsRatioPercentageOnEdit: 40,
216
218
  },
217
219
  calculateStats: false,
220
+ getTextLines: defaultGetTextLines,
221
+ statsCalculator: BasicStatsCalculator,
218
222
  },
219
223
  }
220
224
  ) {
@@ -805,25 +809,10 @@ class PlanarFreehandROITool extends AnnotationTool {
805
809
  const worldPosEnd = imageData.indexToWorld([iMax, jMax, kMax]);
806
810
  const canvasPosEnd = viewport.worldToCanvas(worldPosEnd);
807
811
 
808
- let count = 0;
809
- let sum = 0;
810
- let sumSquares = 0;
811
- let max = -Infinity;
812
-
813
- const statCalculator = ({ value: newValue }) => {
814
- if (newValue > max) {
815
- max = newValue;
816
- }
817
-
818
- sum += newValue;
819
- sumSquares += newValue ** 2;
820
- count += 1;
821
- };
822
-
823
812
  let curRow = 0;
824
813
  let intersections = [];
825
814
  let intersectionCounter = 0;
826
- pointInShapeCallback(
815
+ const pointsInShape = pointInShapeCallback(
827
816
  imageData,
828
817
  (pointLPS, pointIJK) => {
829
818
  let result = true;
@@ -857,28 +846,26 @@ class PlanarFreehandROITool extends AnnotationTool {
857
846
  }
858
847
  return result;
859
848
  },
860
- statCalculator,
849
+ this.configuration.statsCalculator.statsCallback,
861
850
  boundsIJK
862
851
  );
863
852
 
864
- const mean = sum / count;
865
-
866
- // https://www.strchr.com/standard_deviation_in_one_pass?allcomments=1
867
- let stdDev = sumSquares / count - mean ** 2;
868
- stdDev = Math.sqrt(stdDev);
869
-
870
853
  const modalityUnit = getModalityUnit(
871
854
  metadata.Modality,
872
855
  annotation.metadata.referencedImageId,
873
856
  modalityUnitOptions
874
857
  );
875
858
 
859
+ const stats = this.configuration.statsCalculator.getStatistics();
860
+
876
861
  cachedStats[targetId] = {
877
862
  Modality: metadata.Modality,
878
863
  area,
879
- mean,
880
- max,
881
- stdDev,
864
+ mean: stats[1]?.value,
865
+ max: stats[0]?.value,
866
+ stdDev: stats[3]?.value,
867
+ statsArray: stats,
868
+ pointsInShape: pointsInShape,
882
869
  areaUnit: getCalibratedAreaUnits(null, image),
883
870
  modalityUnit,
884
871
  };
@@ -895,7 +882,7 @@ class PlanarFreehandROITool extends AnnotationTool {
895
882
  const data = annotation.data;
896
883
  const targetId = this.getTargetId(viewport);
897
884
 
898
- const textLines = this._getTextLines(data, targetId);
885
+ const textLines = this.configuration.getTextLines(data, targetId);
899
886
  if (!textLines || textLines.length === 0) {
900
887
  return;
901
888
  }
@@ -941,35 +928,35 @@ class PlanarFreehandROITool extends AnnotationTool {
941
928
  bottomRight: viewport.canvasToWorld([left + width, top + height]),
942
929
  };
943
930
  };
931
+ }
944
932
 
945
- _getTextLines = (data, targetId: string): string[] => {
946
- const cachedVolumeStats = data.cachedStats[targetId];
947
- const { area, mean, stdDev, max, isEmptyArea, areaUnit, modalityUnit } =
948
- cachedVolumeStats;
933
+ function defaultGetTextLines(data, targetId): string[] {
934
+ const cachedVolumeStats = data.cachedStats[targetId];
935
+ const { area, mean, stdDev, max, isEmptyArea, areaUnit, modalityUnit } =
936
+ cachedVolumeStats;
949
937
 
950
- const textLines: string[] = [];
938
+ const textLines: string[] = [];
951
939
 
952
- if (area) {
953
- const areaLine = isEmptyArea
954
- ? `Area: Oblique not supported`
955
- : `Area: ${roundNumber(area)} ${areaUnit}`;
956
- textLines.push(areaLine);
957
- }
940
+ if (area) {
941
+ const areaLine = isEmptyArea
942
+ ? `Area: Oblique not supported`
943
+ : `Area: ${roundNumber(area)} ${areaUnit}`;
944
+ textLines.push(areaLine);
945
+ }
958
946
 
959
- if (mean) {
960
- textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
961
- }
947
+ if (mean) {
948
+ textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
949
+ }
962
950
 
963
- if (max) {
964
- textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
965
- }
951
+ if (max) {
952
+ textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
953
+ }
966
954
 
967
- if (stdDev) {
968
- textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
969
- }
955
+ if (stdDev) {
956
+ textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
957
+ }
970
958
 
971
- return textLines;
972
- };
959
+ return textLines;
973
960
  }
974
961
 
975
962
  PlanarFreehandROITool.toolName = 'PlanarFreehandROI';
@@ -94,6 +94,7 @@ const { transformWorldToIndex } = csUtils;
94
94
  * Read more in the Docs section of the website.
95
95
  *
96
96
  */
97
+
97
98
  class ProbeTool extends AnnotationTool {
98
99
  static toolName;
99
100
 
@@ -118,6 +119,7 @@ class ProbeTool extends AnnotationTool {
118
119
  configuration: {
119
120
  shadow: true,
120
121
  preventHandleOutsideImage: false,
122
+ getTextLines: defaultGetTextLines,
121
123
  },
122
124
  }
123
125
  ) {
@@ -525,7 +527,7 @@ class ProbeTool extends AnnotationTool {
525
527
 
526
528
  renderStatus = true;
527
529
 
528
- const textLines = this._getTextLines(data, targetId);
530
+ const textLines = this.configuration.getTextLines(data, targetId);
529
531
  if (textLines) {
530
532
  const textCanvasCoordinates = [
531
533
  canvasCoordinates[0] + 6,
@@ -547,23 +549,6 @@ class ProbeTool extends AnnotationTool {
547
549
  return renderStatus;
548
550
  };
549
551
 
550
- _getTextLines(data, targetId: string): string[] | undefined {
551
- const cachedVolumeStats = data.cachedStats[targetId];
552
- const { index, value, modalityUnit } = cachedVolumeStats;
553
-
554
- if (value === undefined) {
555
- return;
556
- }
557
-
558
- const textLines = [];
559
-
560
- textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);
561
-
562
- textLines.push(`${value.toFixed(2)} ${modalityUnit}`);
563
-
564
- return textLines;
565
- }
566
-
567
552
  _calculateCachedStats(
568
553
  annotation,
569
554
  renderingEngine,
@@ -662,5 +647,22 @@ class ProbeTool extends AnnotationTool {
662
647
  }
663
648
  }
664
649
 
650
+ function defaultGetTextLines(data, targetId): string[] {
651
+ const cachedVolumeStats = data.cachedStats[targetId];
652
+ const { index, value, modalityUnit } = cachedVolumeStats;
653
+
654
+ if (value === undefined) {
655
+ return;
656
+ }
657
+
658
+ const textLines = [];
659
+
660
+ textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);
661
+
662
+ textLines.push(`${value.toFixed(2)} ${modalityUnit}`);
663
+
664
+ return textLines;
665
+ }
666
+
665
667
  ProbeTool.toolName = 'Probe';
666
668
  export default ProbeTool;
@@ -56,6 +56,8 @@ import {
56
56
  import { StyleSpecifier } from '../../types/AnnotationStyle';
57
57
  import { getModalityUnit } from '../../utilities/getModalityUnit';
58
58
  import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
59
+ import { pointInShapeCallback } from '../../utilities/';
60
+ import { BasicStatsCalculator } from '../../utilities/math/basic';
59
61
 
60
62
  const { transformWorldToIndex } = csUtils;
61
63
 
@@ -97,6 +99,7 @@ const { transformWorldToIndex } = csUtils;
97
99
  *
98
100
  * Read more in the Docs section of the website.
99
101
  */
102
+
100
103
  class RectangleROITool extends AnnotationTool {
101
104
  static toolName;
102
105
 
@@ -119,6 +122,8 @@ class RectangleROITool extends AnnotationTool {
119
122
  configuration: {
120
123
  shadow: true,
121
124
  preventHandleOutsideImage: false,
125
+ getTextLines: defaultGetTextLines,
126
+ statsCalculator: BasicStatsCalculator,
122
127
  },
123
128
  }
124
129
  ) {
@@ -786,7 +791,7 @@ class RectangleROITool extends AnnotationTool {
786
791
 
787
792
  renderStatus = true;
788
793
 
789
- const textLines = this._getTextLines(data, targetId);
794
+ const textLines = this.configuration.getTextLines(data, targetId);
790
795
  if (!textLines || textLines.length === 0) {
791
796
  continue;
792
797
  }
@@ -845,33 +850,6 @@ class RectangleROITool extends AnnotationTool {
845
850
  };
846
851
  };
847
852
 
848
- /**
849
- * _getTextLines - Returns the Area, mean and std deviation of the area of the
850
- * target volume enclosed by the rectangle.
851
- *
852
- * @param data - The annotation tool-specific data.
853
- * @param targetId - The volumeId of the volume to display the stats for.
854
- * @param isPreScaled - Whether the viewport is pre-scaled or not.
855
- */
856
- _getTextLines = (data, targetId: string): string[] | undefined => {
857
- const cachedVolumeStats = data.cachedStats[targetId];
858
- const { area, mean, max, stdDev, areaUnit, modalityUnit } =
859
- cachedVolumeStats;
860
-
861
- if (mean === undefined) {
862
- return;
863
- }
864
-
865
- const textLines: string[] = [];
866
-
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}`);
871
-
872
- return textLines;
873
- };
874
-
875
853
  /**
876
854
  * _calculateCachedStats - For each volume in the frame of reference that a
877
855
  * tool instance in particular viewport defines as its target volume, find the
@@ -945,6 +923,12 @@ class RectangleROITool extends AnnotationTool {
945
923
  const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
946
924
  const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
947
925
 
926
+ const boundsIJK = [
927
+ [iMin, iMax],
928
+ [jMin, jMax],
929
+ [kMin, kMax],
930
+ ] as [Types.Point2, Types.Point2, Types.Point2];
931
+
948
932
  const { worldWidth, worldHeight } = getWorldWidthAndHeightFromCorners(
949
933
  viewPlaneNormal,
950
934
  viewUp,
@@ -955,61 +939,29 @@ class RectangleROITool extends AnnotationTool {
955
939
 
956
940
  const area = Math.abs(worldWidth * worldHeight) / (scale * scale);
957
941
 
958
- let count = 0;
959
- let mean = 0;
960
- let stdDev = 0;
961
- let max = -Infinity;
962
-
963
- const yMultiple = dimensions[0];
964
- const zMultiple = dimensions[0] * dimensions[1];
965
-
966
- //Todo: this can be replaced by pointInShapeCallback....
967
- // This is a triple loop, but one of these 3 values will be constant
968
- // In the planar view.
969
- for (let k = kMin; k <= kMax; k++) {
970
- for (let j = jMin; j <= jMax; j++) {
971
- for (let i = iMin; i <= iMax; i++) {
972
- const value = scalarData[k * zMultiple + j * yMultiple + i];
973
-
974
- if (value > max) {
975
- max = value;
976
- }
977
-
978
- count++;
979
- mean += value;
980
- }
981
- }
982
- }
983
-
984
- mean /= count;
985
-
986
- for (let k = kMin; k <= kMax; k++) {
987
- for (let j = jMin; j <= jMax; j++) {
988
- for (let i = iMin; i <= iMax; i++) {
989
- const value = scalarData[k * zMultiple + j * yMultiple + i];
990
-
991
- const valueMinusMean = value - mean;
992
-
993
- stdDev += valueMinusMean * valueMinusMean;
994
- }
995
- }
996
- }
997
-
998
- stdDev /= count;
999
- stdDev = Math.sqrt(stdDev);
1000
-
1001
942
  const modalityUnit = getModalityUnit(
1002
943
  metadata.Modality,
1003
944
  annotation.metadata.referencedImageId,
1004
945
  modalityUnitOptions
1005
946
  );
1006
947
 
948
+ const pointsInShape = pointInShapeCallback(
949
+ imageData,
950
+ () => true,
951
+ this.configuration.statsCalculator.statsCallback,
952
+ boundsIJK
953
+ );
954
+
955
+ const stats = this.configuration.statsCalculator.getStatistics();
956
+
1007
957
  cachedStats[targetId] = {
1008
958
  Modality: metadata.Modality,
1009
959
  area,
1010
- mean,
1011
- stdDev,
1012
- max,
960
+ mean: stats[1]?.value,
961
+ stdDev: stats[2]?.value,
962
+ max: stats[0]?.value,
963
+ statsArray: stats,
964
+ pointsInShape: pointsInShape,
1013
965
  areaUnit: getCalibratedAreaUnits(null, image),
1014
966
  modalityUnit,
1015
967
  };
@@ -1044,5 +996,30 @@ class RectangleROITool extends AnnotationTool {
1044
996
  };
1045
997
  }
1046
998
 
999
+ /**
1000
+ * _getTextLines - Returns the Area, mean and std deviation of the area of the
1001
+ * target volume enclosed by the rectangle.
1002
+ *
1003
+ * @param data - The annotation tool-specific data.
1004
+ * @param targetId - The volumeId of the volume to display the stats for.
1005
+ */
1006
+ function defaultGetTextLines(data, targetId: string): string[] {
1007
+ const cachedVolumeStats = data.cachedStats[targetId];
1008
+ const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
1009
+
1010
+ if (mean === undefined) {
1011
+ return;
1012
+ }
1013
+
1014
+ const textLines: string[] = [];
1015
+
1016
+ textLines.push(`Area: ${roundNumber(area)} ${areaUnit}`);
1017
+ textLines.push(`Mean: ${roundNumber(mean)} ${modalityUnit}`);
1018
+ textLines.push(`Max: ${roundNumber(max)} ${modalityUnit}`);
1019
+ textLines.push(`Std Dev: ${roundNumber(stdDev)} ${modalityUnit}`);
1020
+
1021
+ return textLines;
1022
+ }
1023
+
1047
1024
  RectangleROITool.toolName = 'RectangleROI';
1048
1025
  export default RectangleROITool;
@@ -18,10 +18,12 @@ import {
18
18
  EventTypes,
19
19
  ToolHandle,
20
20
  InteractionTypes,
21
+ ToolProps,
22
+ PublicToolProps,
21
23
  } from '../../types';
22
24
  import { StyleSpecifier } from '../../types/AnnotationStyle';
23
25
 
24
- /**
26
+ /**-q
25
27
  * Abstract class for tools which create and display annotations on the
26
28
  * cornerstone3D canvas. In addition, it provides a base class for segmentation
27
29
  * tools that require drawing an annotation before running the segmentation strategy
@@ -37,6 +39,19 @@ abstract class AnnotationTool extends AnnotationDisplayTool {
37
39
  // Abstract Methods - Must be implemented.
38
40
  // ===================================================================
39
41
 
42
+ constructor(toolProps: PublicToolProps, defaultToolProps: ToolProps) {
43
+ super(toolProps, defaultToolProps);
44
+
45
+ if (toolProps.configuration?.getTextLines) {
46
+ this.configuration.getTextLines = toolProps.configuration.getTextLines;
47
+ }
48
+
49
+ if (toolProps.configuration?.statsCalculator) {
50
+ this.configuration.statsCalculator =
51
+ toolProps.configuration.statsCalculator;
52
+ }
53
+ }
54
+
40
55
  /**
41
56
  * @abstract addNewAnnotation Creates a new annotation based on the clicked mouse position
42
57
  *
@@ -0,0 +1,7 @@
1
+ type Statistics = {
2
+ name: string;
3
+ value: number;
4
+ unit: null | string;
5
+ };
6
+
7
+ export type { Statistics };
@@ -1,6 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import { SetToolBindingsType, ToolOptionsType } from './ISetToolModeOptions';
3
3
  import { MouseBindings } from '../enums';
4
+ import { ToolConfiguration } from '../types';
4
5
  /**
5
6
  * ToolGroup interface
6
7
  */
@@ -19,8 +20,10 @@ export default interface IToolGroup {
19
20
  getViewportsInfo: () => Array<Types.IViewportId>;
20
21
  /** Get the toolInstance of the toolName */
21
22
  getToolInstance: { (toolName: string): any };
22
- /** Add a tool to toolGroup with its configuration */
23
- addTool: { (toolName: string, toolConfiguration?: any): void };
23
+ /** Add a tool to toolGroup with its configuration and custom calculator if wanted */
24
+ addTool: {
25
+ (toolName: string, toolConfiguration?: ToolConfiguration): void;
26
+ };
24
27
  /** Add tool instance, if you want to create more than one instance from the same tool e.g., brush/eraser tool */
25
28
  addToolInstance: {
26
29
  (ttoolName: string, parentClassName: string, configuration?: any): void;
@@ -62,7 +65,7 @@ export default interface IToolGroup {
62
65
  setToolConfiguration: {
63
66
  (
64
67
  toolName: string,
65
- configuration: Record<any, any>,
68
+ configuration: ToolConfiguration,
66
69
  overwrite?: boolean
67
70
  ): void;
68
71
  };
@@ -1,8 +1,14 @@
1
+ import { Calculator } from '../utilities/math/basic';
2
+
1
3
  type SharedToolProp = {
2
4
  /** supported interactions for the tool */
3
5
  supportedInteractionTypes?: Array<string>;
4
6
  /** tool specific tool configuration */
5
- configuration?: Record<string, any>;
7
+ configuration?: ToolConfiguration;
8
+ };
9
+
10
+ export type ToolConfiguration = Record<string, any> & {
11
+ statsCalculator?: Calculator;
6
12
  };
7
13
 
8
14
  export type ToolProps = SharedToolProp;
@@ -21,7 +21,11 @@ import type * as AnnotationStyle from './AnnotationStyle';
21
21
  import type ToolHandle from './ToolHandle';
22
22
  import type { AnnotationHandle, TextBoxHandle } from './ToolHandle';
23
23
  import type InteractionTypes from './InteractionTypes';
24
- import type { ToolProps, PublicToolProps } from './ToolProps';
24
+ import type {
25
+ ToolProps,
26
+ PublicToolProps,
27
+ ToolConfiguration,
28
+ } from './ToolProps';
25
29
  import type { SVGCursorDescriptor, SVGPoint } from './CursorTypes';
26
30
  import type JumpToSliceOptions from './JumpToSliceOptions';
27
31
  import type ScrollOptions from './ScrollOptions';
@@ -52,6 +56,7 @@ import IToolClassReference from './IToolClassReference';
52
56
  import { ContourSegmentationData } from './ContourTypes';
53
57
  import IAnnotationManager from './IAnnotationManager';
54
58
  import AnnotationGroupSelector from './AnnotationGroupSelector';
59
+ import { Statistics } from './CalculatorTypes';
55
60
 
56
61
  export type {
57
62
  // AnnotationState
@@ -68,6 +73,7 @@ export type {
68
73
  PlanarBoundingBox,
69
74
  ToolProps,
70
75
  PublicToolProps,
76
+ ToolConfiguration,
71
77
  // Event data
72
78
  EventTypes,
73
79
  IPoints,
@@ -114,4 +120,6 @@ export type {
114
120
  FloodFillOptions,
115
121
  // Contour
116
122
  ContourSegmentationData,
123
+ //Statistics
124
+ Statistics,
117
125
  };
@@ -0,0 +1,60 @@
1
+ import { Statistics } from '../../../types';
2
+ import Calculator from './Calculator';
3
+
4
+ export default class BasicStatsCalculator extends Calculator {
5
+ private static max = -Infinity;
6
+ private static currentMax = 0;
7
+ private static sum = 0;
8
+ private static sumSquares = 0;
9
+ private static squaredDiffSum = 0;
10
+ private static count = 0;
11
+
12
+ /**
13
+ * This callback is used when we verify if the point is in the annotion drawn so we can get every point
14
+ * in the shape to calculate the statistics
15
+ * @param value of the point in the shape of the annotation
16
+ */
17
+ static statsCallback = ({ value: newValue }): void => {
18
+ if (newValue > this.max) {
19
+ this.max = newValue;
20
+ this.currentMax = newValue;
21
+ }
22
+
23
+ this.count += 1;
24
+
25
+ this.sum += newValue;
26
+ this.sumSquares += newValue ** 2;
27
+ this.squaredDiffSum += Math.pow(newValue - this.sum / this.count, 2);
28
+ };
29
+
30
+ /**
31
+ * Basic function that calculates statictics for a given array of points.
32
+ * @param points
33
+ * @returns An object that contains :
34
+ * max : The maximum value of the array
35
+ * mean : mean of the array
36
+ * stdDev : standard deviation of the array
37
+ * stdDevWithSumSquare : standard deviation of the array using sum²
38
+ */
39
+
40
+ static getStatistics = (): Statistics[] => {
41
+ const mean = this.sum / this.count;
42
+ const stdDev = Math.sqrt(this.squaredDiffSum / this.count);
43
+ const stdDevWithSumSquare = Math.sqrt(
44
+ this.sumSquares / this.count - mean ** 2
45
+ );
46
+
47
+ this.max = -Infinity;
48
+ this.sum = 0;
49
+ this.sumSquares = 0;
50
+ this.squaredDiffSum = 0;
51
+ this.count = 0;
52
+
53
+ return [
54
+ { name: 'max', value: this.currentMax, unit: null },
55
+ { name: 'mean', value: mean, unit: null },
56
+ { name: 'stdDev', value: stdDev, unit: null },
57
+ { name: 'stdDevWithSumSquare', value: stdDevWithSumSquare, unit: null },
58
+ ];
59
+ };
60
+ }
@@ -0,0 +1,8 @@
1
+ import { Statistics } from '../../../types';
2
+
3
+ abstract class Calculator {
4
+ static run: ({ value }) => void;
5
+ static getStatistics: () => Statistics[];
6
+ }
7
+
8
+ export default Calculator;
@@ -0,0 +1,4 @@
1
+ import BasicStatsCalculator from './BasicStatsCalculator';
2
+ import Calculator from './Calculator';
3
+
4
+ export { BasicStatsCalculator, Calculator };
@@ -4,5 +4,14 @@ import * as lineSegment from './line';
4
4
  import * as rectangle from './rectangle';
5
5
  import * as polyline from './polyline';
6
6
  import * as point from './point';
7
+ import * as BasicStatsCalculator from './basic';
7
8
 
8
- export { vec2, ellipse, lineSegment, rectangle, polyline, point };
9
+ export {
10
+ vec2,
11
+ ellipse,
12
+ lineSegment,
13
+ rectangle,
14
+ polyline,
15
+ point,
16
+ BasicStatsCalculator,
17
+ };