@cornerstonejs/tools 4.12.4 → 4.12.6

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.
@@ -0,0 +1,6 @@
1
+ export declare enum MeasurementType {
2
+ Linear = "Linear",
3
+ Area = "Area",
4
+ Volume = "Volume",
5
+ Pixel = "Pixel"
6
+ }
@@ -0,0 +1,7 @@
1
+ export var MeasurementType;
2
+ (function (MeasurementType) {
3
+ MeasurementType["Linear"] = "Linear";
4
+ MeasurementType["Area"] = "Area";
5
+ MeasurementType["Volume"] = "Volume";
6
+ MeasurementType["Pixel"] = "Pixel";
7
+ })(MeasurementType || (MeasurementType = {}));
@@ -1,3 +1,4 @@
1
+ export * from "./MeasurementType";
1
2
  import { MouseBindings } from './ToolBindings';
2
3
  import { KeyboardBindings } from './ToolBindings';
3
4
  import ToolModes from './ToolModes';
@@ -7,4 +7,5 @@ import { Swipe } from './Touch';
7
7
  import StrategyCallbacks from './StrategyCallbacks';
8
8
  import ChangeTypes from './ChangeTypes';
9
9
  import WorkerTypes from './WorkerTypes';
10
+ export * from './MeasurementType';
10
11
  export { MouseBindings, KeyboardBindings, ToolModes, AnnotationStyleStates, Events, SegmentationRepresentations, Swipe, StrategyCallbacks, ChangeTypes, WorkerTypes, };
@@ -39,9 +39,7 @@ declare class BidirectionalTool extends AnnotationTool {
39
39
  _deactivateModify: (element: any) => void;
40
40
  renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
41
41
  _movingLongAxisWouldPutItThroughShortAxis: (firstLineSegment: any, secondLineSegment: any) => boolean;
42
- _calculateLength(pos1: any, pos2: any): number;
43
42
  _calculateCachedStats: (annotation: any, renderingEngine: any, enabledElement: any) => any;
44
- _isInsideVolume: (index1: any, index2: any, index3: any, index4: any, dimensions: any) => boolean;
45
43
  _getSignedAngle: (vector1: any, vector2: any) => number;
46
44
  }
47
45
  export default BidirectionalTool;
@@ -9,7 +9,7 @@ import { isAnnotationVisible } from '../../stateManagement/annotation/annotation
9
9
  import { triggerAnnotationCompleted, triggerAnnotationModified, } from '../../stateManagement/annotation/helpers/state';
10
10
  import { drawLine as drawLineSvg, drawHandles as drawHandlesSvg, drawLinkedTextBox as drawLinkedTextBoxSvg, } from '../../drawingSvg';
11
11
  import { state } from '../../store/state';
12
- import { ChangeTypes, Events } from '../../enums';
12
+ import { ChangeTypes, Events, MeasurementType } from '../../enums';
13
13
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
14
14
  import * as lineSegment from '../../utilities/math/line';
15
15
  import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
@@ -610,28 +610,36 @@ class BidirectionalTool extends AnnotationTool {
610
610
  continue;
611
611
  }
612
612
  const { imageData, dimensions } = image;
613
- const index1 = transformWorldToIndex(imageData, worldPos1);
614
- const index2 = transformWorldToIndex(imageData, worldPos2);
615
- const index3 = transformWorldToIndex(imageData, worldPos3);
616
- const index4 = transformWorldToIndex(imageData, worldPos4);
617
- const handles1 = [index1, index2];
618
- const handles2 = [index3, index4];
619
- const { scale: scale1, unit: units1 } = getCalibratedLengthUnitsAndScale(image, handles1);
620
- const { scale: scale2, unit: units2 } = getCalibratedLengthUnitsAndScale(image, handles2);
621
- const dist1 = this._calculateLength(worldPos1, worldPos2) / scale1;
622
- const dist2 = this._calculateLength(worldPos3, worldPos4) / scale2;
613
+ const handles = data.handles.points.map((point) => imageData.worldToIndex(point));
614
+ const handles1 = handles.slice(0, 2);
615
+ const handles2 = handles.slice(2, 4);
616
+ const calibrate = getCalibratedLengthUnitsAndScale(image, handles);
617
+ const dist1 = BidirectionalTool.calculateLengthInIndex(calibrate, handles1);
618
+ const dist2 = BidirectionalTool.calculateLengthInIndex(calibrate, handles2);
619
+ const { unit } = calibrate;
623
620
  const length = dist1 > dist2 ? dist1 : dist2;
624
621
  const width = dist1 > dist2 ? dist2 : dist1;
625
- const unit = dist1 > dist2 ? units1 : units2;
626
- const widthUnit = dist1 > dist2 ? units2 : units1;
627
- this._isInsideVolume(index1, index2, index3, index4, dimensions)
628
- ? (this.isHandleOutsideImage = false)
629
- : (this.isHandleOutsideImage = true);
622
+ const widthUnit = unit;
623
+ this.isHandleOutsideImage = !BidirectionalTool.isInsideVolume(dimensions, handles);
630
624
  cachedStats[targetId] = {
631
625
  length,
632
626
  width,
633
627
  unit,
634
628
  widthUnit,
629
+ statsArray: [
630
+ {
631
+ value: length,
632
+ name: 'height',
633
+ unit,
634
+ type: MeasurementType.Linear,
635
+ },
636
+ {
637
+ value: width,
638
+ name: 'width',
639
+ unit,
640
+ type: MeasurementType.Linear,
641
+ },
642
+ ],
635
643
  };
636
644
  }
637
645
  const invalidated = annotation.invalidated;
@@ -641,12 +649,6 @@ class BidirectionalTool extends AnnotationTool {
641
649
  }
642
650
  return cachedStats;
643
651
  };
644
- this._isInsideVolume = (index1, index2, index3, index4, dimensions) => {
645
- return (csUtils.indexWithinDimensions(index1, dimensions) &&
646
- csUtils.indexWithinDimensions(index2, dimensions) &&
647
- csUtils.indexWithinDimensions(index3, dimensions) &&
648
- csUtils.indexWithinDimensions(index4, dimensions));
649
- };
650
652
  this._getSignedAngle = (vector1, vector2) => {
651
653
  return Math.atan2(vector1[0] * vector2[1] - vector1[1] * vector2[0], vector1[0] * vector2[0] + vector1[1] * vector2[1]);
652
654
  };
@@ -726,12 +728,6 @@ class BidirectionalTool extends AnnotationTool {
726
728
  triggerAnnotationRenderForViewportIds([viewport.id]);
727
729
  return annotation;
728
730
  }; }
729
- _calculateLength(pos1, pos2) {
730
- const dx = pos1[0] - pos2[0];
731
- const dy = pos1[1] - pos2[1];
732
- const dz = pos1[2] - pos2[2];
733
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
734
- }
735
731
  }
736
732
  function defaultGetTextLines(data, targetId) {
737
733
  const { cachedStats, label } = data;
@@ -30,8 +30,7 @@ declare class CircleROITool extends AnnotationTool {
30
30
  _activateDraw: (element: any) => void;
31
31
  _deactivateDraw: (element: any) => void;
32
32
  renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
33
- _calculateCachedStats: (annotation: any, viewport: any, renderingEngine: any, enabledElement: any) => any;
34
- _isInsideVolume: (index1: any, index2: any, dimensions: any) => boolean;
33
+ _calculateCachedStats: (annotation: any, viewport: any, _renderingEngine: any, _enabledElement: any) => any;
35
34
  static hydrate: (viewportId: string, points: Types.Point3[], options?: {
36
35
  annotationUID?: string;
37
36
  toolInstance?: CircleROITool;
@@ -1,6 +1,7 @@
1
- import { AnnotationTool } from '../base';
1
+ import { AnnotationTool, BaseTool } from '../base';
2
+ import { vec2, vec3 } from 'gl-matrix';
2
3
  import { getEnabledElement, VolumeViewport, utilities as csUtils, getEnabledElementByViewportId, EPSILON, } from '@cornerstonejs/core';
3
- import { getCalibratedAspect, getCalibratedLengthUnitsAndScale, } from '../../utilities/getCalibratedUnits';
4
+ import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
4
5
  import throttle from '../../utilities/throttle';
5
6
  import { addAnnotation, getAnnotations, removeAnnotation, } from '../../stateManagement/annotation/annotationState';
6
7
  import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
@@ -8,10 +9,9 @@ import { isAnnotationVisible } from '../../stateManagement/annotation/annotation
8
9
  import { triggerAnnotationCompleted, triggerAnnotationModified, } from '../../stateManagement/annotation/helpers/state';
9
10
  import { drawCircle as drawCircleSvg, drawHandles as drawHandlesSvg, drawLinkedTextBox as drawLinkedTextBoxSvg, } from '../../drawingSvg';
10
11
  import { state } from '../../store/state';
11
- import { ChangeTypes, Events } from '../../enums';
12
+ import { ChangeTypes, Events, MeasurementType } from '../../enums';
12
13
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
13
14
  import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
14
- import getWorldWidthAndHeightFromTwoPoints from '../../utilities/planar/getWorldWidthAndHeightFromTwoPoints';
15
15
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
16
16
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
17
17
  import { getPixelValueUnits } from '../../utilities/getPixelValueUnits';
@@ -19,7 +19,6 @@ import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScale
19
19
  import { getCanvasCircleCorners, getCanvasCircleRadius, } from '../../utilities/math/circle';
20
20
  import { pointInEllipse } from '../../utilities/math/ellipse';
21
21
  import { BasicStatsCalculator } from '../../utilities/math/basic';
22
- import { vec2, vec3 } from 'gl-matrix';
23
22
  import { getStyleProperty } from '../../stateManagement/annotation/config/helpers';
24
23
  const { transformWorldToIndex } = csUtils;
25
24
  class CircleROITool extends AnnotationTool {
@@ -473,25 +472,22 @@ class CircleROITool extends AnnotationTool {
473
472
  }
474
473
  return renderStatus;
475
474
  };
476
- this._calculateCachedStats = (annotation, viewport, renderingEngine, enabledElement) => {
475
+ this._calculateCachedStats = (annotation, viewport, _renderingEngine, _enabledElement) => {
477
476
  if (!this.configuration.calculateStats) {
478
477
  return;
479
478
  }
480
- const data = annotation.data;
479
+ const { data } = annotation;
481
480
  const { element } = viewport;
482
481
  const wasInvalidated = annotation.invalidated;
483
482
  const { points } = data.handles;
484
483
  const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
485
484
  const canvasCenter = canvasCoordinates[0];
486
485
  const canvasTop = canvasCoordinates[1];
487
- const { viewPlaneNormal, viewUp } = viewport.getCamera();
488
486
  const [topLeftCanvas, bottomRightCanvas] = (getCanvasCircleCorners([canvasCenter, canvasTop]));
489
487
  const topLeftWorld = viewport.canvasToWorld(topLeftCanvas);
490
488
  const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas);
491
489
  const { cachedStats } = data;
492
490
  const targetIds = Object.keys(cachedStats);
493
- const worldPos1 = topLeftWorld;
494
- const worldPos2 = bottomRightWorld;
495
491
  for (let i = 0; i < targetIds.length; i++) {
496
492
  const targetId = targetIds[i];
497
493
  const image = this.getTargetImageData(targetId);
@@ -499,15 +495,49 @@ class CircleROITool extends AnnotationTool {
499
495
  continue;
500
496
  }
501
497
  const { dimensions, imageData, metadata, voxelManager } = image;
502
- const pos1Index = transformWorldToIndex(imageData, worldPos1);
503
- pos1Index[0] = Math.floor(pos1Index[0]);
504
- pos1Index[1] = Math.floor(pos1Index[1]);
505
- pos1Index[2] = Math.floor(pos1Index[2]);
506
- const pos2Index = transformWorldToIndex(imageData, worldPos2);
507
- pos2Index[0] = Math.floor(pos2Index[0]);
508
- pos2Index[1] = Math.floor(pos2Index[1]);
509
- pos2Index[2] = Math.floor(pos2Index[2]);
510
- if (this._isInsideVolume(pos1Index, pos2Index, dimensions)) {
498
+ const handles = points.map((point) => imageData.worldToIndex(point));
499
+ const calibrate = getCalibratedLengthUnitsAndScale(image, handles);
500
+ const radius = CircleROITool.calculateLengthInIndex(calibrate, handles);
501
+ const area = Math.PI * radius * radius;
502
+ const perimeter = 2 * Math.PI * radius;
503
+ const isEmptyArea = radius === 0;
504
+ const { unit, areaUnit } = calibrate;
505
+ const namedArea = {
506
+ name: 'area',
507
+ value: area,
508
+ unit: areaUnit,
509
+ type: MeasurementType.Area,
510
+ };
511
+ const namedCircumference = {
512
+ name: 'circumference',
513
+ value: perimeter,
514
+ unit,
515
+ type: MeasurementType.Linear,
516
+ };
517
+ const namedRadius = {
518
+ name: 'radius',
519
+ value: radius,
520
+ unit,
521
+ type: MeasurementType.Linear,
522
+ };
523
+ const statsArray = [namedArea, namedRadius, namedCircumference];
524
+ cachedStats[targetId] = {
525
+ Modality: metadata.Modality,
526
+ area,
527
+ isEmptyArea,
528
+ areaUnit,
529
+ radius,
530
+ radiusUnit: unit,
531
+ perimeter,
532
+ statsArray,
533
+ };
534
+ const pos1Index = transformWorldToIndex(imageData, topLeftWorld);
535
+ const pos2Index = transformWorldToIndex(imageData, bottomRightWorld);
536
+ this.isHandleOutsideImage = !BaseTool.isInsideVolume(dimensions, [
537
+ pos1Index,
538
+ pos2Index,
539
+ ]);
540
+ if (!this.isHandleOutsideImage) {
511
541
  const iMin = Math.min(pos1Index[0], pos2Index[0]);
512
542
  const iMax = Math.max(pos1Index[0], pos2Index[0]);
513
543
  const jMin = Math.min(pos1Index[1], pos2Index[1]);
@@ -529,14 +559,6 @@ class CircleROITool extends AnnotationTool {
529
559
  yRadius: yRadius < EPSILON / 2 ? 0 : yRadius,
530
560
  zRadius: zRadius < EPSILON / 2 ? 0 : zRadius,
531
561
  };
532
- const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(viewPlaneNormal, viewUp, worldPos1, worldPos2);
533
- const isEmptyArea = worldWidth === 0 && worldHeight === 0;
534
- const handles = [pos1Index, pos2Index];
535
- const { scale, unit, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
536
- const aspect = getCalibratedAspect(image);
537
- const area = Math.abs(Math.PI *
538
- (worldWidth / scale / 2) *
539
- (worldHeight / aspect / scale / 2));
540
562
  const pixelUnitsOptions = {
541
563
  isPreScaled: isViewportPreScaled(viewport, targetId),
542
564
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
@@ -553,26 +575,15 @@ class CircleROITool extends AnnotationTool {
553
575
  }
554
576
  const stats = this.configuration.statsCalculator.getStatistics();
555
577
  cachedStats[targetId] = {
578
+ ...cachedStats[targetId],
556
579
  Modality: metadata.Modality,
557
- area,
558
580
  mean: stats.mean?.value,
559
581
  max: stats.max?.value,
560
582
  min: stats.min?.value,
561
583
  pointsInShape,
562
584
  stdDev: stats.stdDev?.value,
563
- statsArray: stats.array,
564
- isEmptyArea,
565
- areaUnit,
566
- radius: worldWidth / 2 / scale,
567
- radiusUnit: unit,
568
- perimeter: (2 * Math.PI * (worldWidth / 2)) / scale,
569
585
  modalityUnit,
570
- };
571
- }
572
- else {
573
- this.isHandleOutsideImage = true;
574
- cachedStats[targetId] = {
575
- Modality: metadata.Modality,
586
+ statsArray: [...statsArray, ...stats.array],
576
587
  };
577
588
  }
578
589
  }
@@ -582,10 +593,6 @@ class CircleROITool extends AnnotationTool {
582
593
  }
583
594
  return cachedStats;
584
595
  };
585
- this._isInsideVolume = (index1, index2, dimensions) => {
586
- return (csUtils.indexWithinDimensions(index1, dimensions) &&
587
- csUtils.indexWithinDimensions(index2, dimensions));
588
- };
589
596
  this._throttledCalculateCachedStats = throttle(this._calculateCachedStats, 100, { trailing: true });
590
597
  }
591
598
  static { this.hydrate = (viewportId, points, options) => {
@@ -568,7 +568,7 @@ class EllipticalROITool extends AnnotationTool {
568
568
  const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints(viewPlaneNormal, viewUp, worldPos1, worldPos2);
569
569
  const isEmptyArea = worldWidth === 0 && worldHeight === 0;
570
570
  const handles = [pos1Index, pos2Index];
571
- const { scale, unit, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
571
+ const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
572
572
  const aspect = getCalibratedAspect(image);
573
573
  const area = Math.abs(Math.PI *
574
574
  (worldWidth / scale / 2) *
@@ -35,8 +35,6 @@ declare class LengthTool extends AnnotationTool {
35
35
  _activateDraw: (element: HTMLDivElement) => void;
36
36
  _deactivateDraw: (element: HTMLDivElement) => void;
37
37
  renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
38
- _calculateLength(pos1: any, pos2: any): number;
39
38
  _calculateCachedStats(annotation: any, renderingEngine: any, enabledElement: any): any;
40
- _isInsideVolume(index1: any, index2: any, dimensions: any): boolean;
41
39
  }
42
40
  export default LengthTool;
@@ -1,4 +1,4 @@
1
- import { Events, ChangeTypes } from '../../enums';
1
+ import { Events, ChangeTypes, MeasurementType } from '../../enums';
2
2
  import { getEnabledElement, utilities as csUtils, utilities, getEnabledElementByViewportId, } from '@cornerstonejs/core';
3
3
  import { getCalibratedLengthUnitsAndScale } from '../../utilities/getCalibratedUnits';
4
4
  import { AnnotationTool } from '../base';
@@ -15,7 +15,6 @@ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
15
15
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
16
16
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
17
17
  import { getStyleProperty } from '../../stateManagement/annotation/config/helpers';
18
- const { transformWorldToIndex } = csUtils;
19
18
  class LengthTool extends AnnotationTool {
20
19
  static { this.toolName = 'Length'; }
21
20
  constructor(toolProps = {}, defaultToolProps = {
@@ -384,17 +383,9 @@ class LengthTool extends AnnotationTool {
384
383
  triggerAnnotationRenderForViewportIds(viewportIdsToRender);
385
384
  evt.preventDefault();
386
385
  }
387
- _calculateLength(pos1, pos2) {
388
- const dx = pos1[0] - pos2[0];
389
- const dy = pos1[1] - pos2[1];
390
- const dz = pos1[2] - pos2[2];
391
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
392
- }
393
386
  _calculateCachedStats(annotation, renderingEngine, enabledElement) {
394
387
  const data = annotation.data;
395
388
  const { element } = enabledElement.viewport;
396
- const worldPos1 = data.handles.points[0];
397
- const worldPos2 = data.handles.points[1];
398
389
  const { cachedStats } = data;
399
390
  const targetIds = Object.keys(cachedStats);
400
391
  for (let i = 0; i < targetIds.length; i++) {
@@ -404,20 +395,21 @@ class LengthTool extends AnnotationTool {
404
395
  continue;
405
396
  }
406
397
  const { imageData, dimensions } = image;
407
- const index1 = transformWorldToIndex(imageData, worldPos1);
408
- const index2 = transformWorldToIndex(imageData, worldPos2);
409
- const handles = [index1, index2];
410
- const { scale, unit } = getCalibratedLengthUnitsAndScale(image, handles);
411
- const length = this._calculateLength(worldPos1, worldPos2) / scale;
412
- if (this._isInsideVolume(index1, index2, dimensions)) {
413
- this.isHandleOutsideImage = false;
414
- }
415
- else {
416
- this.isHandleOutsideImage = true;
417
- }
398
+ const handles = data.handles.points.map((point) => imageData.worldToIndex(point));
399
+ const calibrate = getCalibratedLengthUnitsAndScale(image, handles);
400
+ const { unit } = calibrate;
401
+ const length = LengthTool.calculateLengthInIndex(calibrate, handles);
402
+ this.isHandleOutsideImage = !LengthTool.isInsideVolume(dimensions, handles);
403
+ const namedLength = {
404
+ name: 'length',
405
+ value: length,
406
+ unit,
407
+ type: MeasurementType.Linear,
408
+ };
418
409
  cachedStats[targetId] = {
419
410
  length,
420
411
  unit,
412
+ statsArray: [namedLength],
421
413
  };
422
414
  }
423
415
  const invalidated = annotation.invalidated;
@@ -427,10 +419,6 @@ class LengthTool extends AnnotationTool {
427
419
  }
428
420
  return cachedStats;
429
421
  }
430
- _isInsideVolume(index1, index2, dimensions) {
431
- return (csUtils.indexWithinDimensions(index1, dimensions) &&
432
- csUtils.indexWithinDimensions(index2, dimensions));
433
- }
434
422
  }
435
423
  function defaultGetTextLines(data, targetId) {
436
424
  const cachedVolumeStats = data.cachedStats[targetId];
@@ -48,12 +48,13 @@ declare class PlanarFreehandROITool extends ContourSegmentationBaseTool {
48
48
  deltaInX: any;
49
49
  deltaInY: any;
50
50
  }): void;
51
- protected updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, points, }: {
51
+ protected updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, imageData, points, }: {
52
52
  targetId: any;
53
53
  metadata: any;
54
54
  cachedStats: any;
55
55
  modalityUnit: any;
56
56
  calibratedScale: any;
57
+ imageData: any;
57
58
  points: any;
58
59
  }): void;
59
60
  private _renderStats;
@@ -19,9 +19,8 @@ import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
19
19
  import { getLineSegmentIntersectionsCoordinates } from '../../utilities/math/polyline';
20
20
  import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
21
21
  import { BasicStatsCalculator } from '../../utilities/math/basic';
22
- import calculatePerimeter from '../../utilities/contours/calculatePerimeter';
23
22
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
24
- import { KeyboardBindings, ChangeTypes } from '../../enums';
23
+ import { KeyboardBindings, ChangeTypes, MeasurementType } from '../../enums';
25
24
  import { getPixelValueUnits } from '../../utilities/getPixelValueUnits';
26
25
  const { pointCanProjectOnLine } = polyline;
27
26
  const { EPSILON } = CONSTANTS;
@@ -150,23 +149,22 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
150
149
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
151
150
  };
152
151
  const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
153
- const calibratedScale = getCalibratedLengthUnitsAndScale(image, () => {
154
- const polyline = data.contour.polyline;
155
- const numPoints = polyline.length;
156
- const projectedPolyline = new Array(numPoints);
157
- for (let i = 0; i < numPoints; i++) {
158
- projectedPolyline[i] = viewport.worldToCanvas(polyline[i]);
159
- }
160
- const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(projectedPolyline);
161
- const topLeftBBWorld = viewport.canvasToWorld([canvasMinX, canvasMinY]);
162
- const topLeftBBIndex = csUtils.transformWorldToIndex(imageData, topLeftBBWorld);
163
- const bottomRightBBWorld = viewport.canvasToWorld([
164
- canvasMaxX,
165
- canvasMaxY,
166
- ]);
167
- const bottomRightBBIndex = csUtils.transformWorldToIndex(imageData, bottomRightBBWorld);
168
- return [topLeftBBIndex, bottomRightBBIndex];
169
- });
152
+ const polyline = data.contour.polyline;
153
+ const numPoints = polyline.length;
154
+ const projectedPolyline = new Array(numPoints);
155
+ for (let i = 0; i < numPoints; i++) {
156
+ projectedPolyline[i] = viewport.worldToCanvas(polyline[i]);
157
+ }
158
+ const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(projectedPolyline);
159
+ const topLeftBBWorld = viewport.canvasToWorld([canvasMinX, canvasMinY]);
160
+ const topLeftBBIndex = csUtils.transformWorldToIndex(imageData, topLeftBBWorld);
161
+ const bottomRightBBWorld = viewport.canvasToWorld([
162
+ canvasMaxX,
163
+ canvasMaxY,
164
+ ]);
165
+ const bottomRightBBIndex = csUtils.transformWorldToIndex(imageData, bottomRightBBWorld);
166
+ const handles = [topLeftBBIndex, bottomRightBBIndex];
167
+ const calibratedScale = getCalibratedLengthUnitsAndScale(image, handles);
170
168
  const canvasPoint = canvasCoordinates[0];
171
169
  const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
172
170
  const deltaXPoint = viewport.canvasToWorld([
@@ -179,30 +177,24 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
179
177
  ]);
180
178
  const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
181
179
  const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
180
+ const statsArgs = {
181
+ targetId,
182
+ viewport,
183
+ canvasCoordinates,
184
+ points,
185
+ imageData,
186
+ metadata,
187
+ cachedStats,
188
+ modalityUnit,
189
+ calibratedScale,
190
+ deltaInX,
191
+ deltaInY,
192
+ };
182
193
  if (closed) {
183
- this.updateClosedCachedStats({
184
- targetId,
185
- viewport,
186
- canvasCoordinates,
187
- points,
188
- imageData,
189
- metadata,
190
- cachedStats,
191
- modalityUnit,
192
- calibratedScale,
193
- deltaInX,
194
- deltaInY,
195
- });
194
+ this.updateClosedCachedStats(statsArgs);
196
195
  }
197
196
  else {
198
- this.updateOpenCachedStats({
199
- metadata,
200
- targetId,
201
- cachedStats,
202
- modalityUnit,
203
- calibratedScale,
204
- points,
205
- });
197
+ this.updateOpenCachedStats(statsArgs);
206
198
  }
207
199
  }
208
200
  const invalidated = annotation.invalidated;
@@ -432,21 +424,15 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
432
424
  updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId, modalityUnit, canvasCoordinates, calibratedScale, deltaInX, deltaInY, }) {
433
425
  const { scale, areaUnit, unit } = calibratedScale;
434
426
  const { voxelManager } = viewport.getImageData();
435
- const worldPosIndex = csUtils.transformWorldToIndex(imageData, points[0]);
436
- worldPosIndex[0] = Math.floor(worldPosIndex[0]);
437
- worldPosIndex[1] = Math.floor(worldPosIndex[1]);
438
- worldPosIndex[2] = Math.floor(worldPosIndex[2]);
439
- let iMin = worldPosIndex[0];
440
- let iMax = worldPosIndex[0];
441
- let jMin = worldPosIndex[1];
442
- let jMax = worldPosIndex[1];
443
- let kMin = worldPosIndex[2];
444
- let kMax = worldPosIndex[2];
445
- for (let j = 1; j < points.length; j++) {
446
- const worldPosIndex = csUtils.transformWorldToIndex(imageData, points[j]);
447
- worldPosIndex[0] = Math.floor(worldPosIndex[0]);
448
- worldPosIndex[1] = Math.floor(worldPosIndex[1]);
449
- worldPosIndex[2] = Math.floor(worldPosIndex[2]);
427
+ const indexPoints = points.map((point) => imageData.worldToIndex(point));
428
+ let iMin = Number.MAX_SAFE_INTEGER;
429
+ let iMax = Number.MIN_SAFE_INTEGER;
430
+ let jMin = Number.MAX_SAFE_INTEGER;
431
+ let jMax = Number.MIN_SAFE_INTEGER;
432
+ let kMin = Number.MAX_SAFE_INTEGER;
433
+ let kMax = Number.MIN_SAFE_INTEGER;
434
+ for (let j = 0; j < points.length; j++) {
435
+ const worldPosIndex = indexPoints[j].map(Math.floor);
450
436
  iMin = Math.min(iMin, worldPosIndex[0]);
451
437
  iMax = Math.max(iMax, worldPosIndex[0]);
452
438
  jMin = Math.min(jMin, worldPosIndex[1]);
@@ -454,13 +440,9 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
454
440
  kMin = Math.min(kMin, worldPosIndex[2]);
455
441
  kMax = Math.max(kMax, worldPosIndex[2]);
456
442
  }
457
- const worldPosIndex2 = csUtils.transformWorldToIndex(imageData, points[1]);
458
- worldPosIndex2[0] = Math.floor(worldPosIndex2[0]);
459
- worldPosIndex2[1] = Math.floor(worldPosIndex2[1]);
460
- worldPosIndex2[2] = Math.floor(worldPosIndex2[2]);
461
443
  let area = polyline.getArea(canvasCoordinates) / scale / scale;
462
444
  area *= deltaInX * deltaInY;
463
- const perimeter = calculatePerimeter(points, closed) / scale;
445
+ const perimeter = PlanarFreehandROITool.calculateLengthInIndex(calibratedScale, indexPoints, closed);
464
446
  const iDelta = 0.01 * (iMax - iMin);
465
447
  const jDelta = 0.01 * (jMax - jMin);
466
448
  const kDelta = 0.01 * (kMax - kMin);
@@ -515,6 +497,18 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
515
497
  });
516
498
  }
517
499
  const stats = this.configuration.statsCalculator.getStatistics();
500
+ const namedArea = {
501
+ name: 'area',
502
+ value: area,
503
+ unit: areaUnit,
504
+ type: MeasurementType.Area,
505
+ };
506
+ const namedPerimeter = {
507
+ name: 'perimeter',
508
+ value: perimeter,
509
+ unit,
510
+ type: MeasurementType.Linear,
511
+ };
518
512
  cachedStats[targetId] = {
519
513
  Modality: metadata.Modality,
520
514
  area,
@@ -523,21 +517,29 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
523
517
  max: stats.max?.value,
524
518
  min: stats.min?.value,
525
519
  stdDev: stats.stdDev?.value,
526
- statsArray: stats.array,
520
+ statsArray: [namedArea, namedPerimeter, ...stats.array],
527
521
  pointsInShape: pointsInShape,
528
522
  areaUnit,
529
523
  modalityUnit,
530
524
  unit,
531
525
  };
532
526
  }
533
- updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, points, }) {
534
- const { scale, unit } = calibratedScale;
535
- const length = calculatePerimeter(points, closed) / scale;
527
+ updateOpenCachedStats({ targetId, metadata, cachedStats, modalityUnit, calibratedScale, imageData, points, }) {
528
+ const { unit } = calibratedScale;
529
+ const indexPoints = points.map((point) => imageData.worldToIndex(point));
530
+ const length = PlanarFreehandROITool.calculateLengthInIndex(calibratedScale, indexPoints);
531
+ const namedLength = {
532
+ name: 'length',
533
+ value: length,
534
+ unit,
535
+ type: MeasurementType.Linear,
536
+ };
536
537
  cachedStats[targetId] = {
537
538
  Modality: metadata.Modality,
538
539
  length,
539
540
  modalityUnit,
540
541
  unit,
542
+ statArray: [namedLength],
541
543
  };
542
544
  }
543
545
  }
@@ -1,4 +1,4 @@
1
- import { utilities } from '@cornerstonejs/core';
1
+ import { utilities as csUtils } from '@cornerstonejs/core';
2
2
  import type { Types } from '@cornerstonejs/core';
3
3
  import ToolModes from '../../enums/ToolModes';
4
4
  import type StrategyCallbacks from '../../enums/StrategyCallbacks';
@@ -9,7 +9,7 @@ declare abstract class BaseTool {
9
9
  configuration: Record<string, any>;
10
10
  toolGroupId: string;
11
11
  mode: ToolModes;
12
- protected memo: utilities.HistoryMemo.Memo;
12
+ protected memo: csUtils.HistoryMemo.Memo;
13
13
  static defaults: {
14
14
  configuration: {
15
15
  strategies: {};
@@ -36,5 +36,7 @@ declare abstract class BaseTool {
36
36
  doneEditMemo(): void;
37
37
  static startGroupRecording(): void;
38
38
  static endGroupRecording(): void;
39
+ static calculateLengthInIndex(calibrate: any, indexPoints: any, closed?: boolean): number;
40
+ static isInsideVolume(dimensions: any, indexPoints: any): boolean;
39
41
  }
40
42
  export default BaseTool;
@@ -1,6 +1,6 @@
1
- import { utilities } from '@cornerstonejs/core';
1
+ import { utilities as csUtils } from '@cornerstonejs/core';
2
2
  import ToolModes from '../../enums/ToolModes';
3
- const { DefaultHistoryMemo } = utilities.HistoryMemo;
3
+ const { DefaultHistoryMemo } = csUtils.HistoryMemo;
4
4
  class BaseTool {
5
5
  static { this.defaults = {
6
6
  configuration: {
@@ -12,7 +12,7 @@ class BaseTool {
12
12
  }; }
13
13
  constructor(toolProps, defaultToolProps) {
14
14
  const mergedDefaults = BaseTool.mergeDefaultProps(BaseTool.defaults, defaultToolProps);
15
- const initialProps = utilities.deepMerge(mergedDefaults, toolProps);
15
+ const initialProps = csUtils.deepMerge(mergedDefaults, toolProps);
16
16
  const { configuration = {}, supportedInteractionTypes, toolGroupId, } = initialProps;
17
17
  this.toolGroupId = toolGroupId;
18
18
  this.supportedInteractionTypes = supportedInteractionTypes || [];
@@ -23,7 +23,7 @@ class BaseTool {
23
23
  if (!additionalProps) {
24
24
  return defaultProps;
25
25
  }
26
- return utilities.deepMerge(defaultProps, additionalProps);
26
+ return csUtils.deepMerge(defaultProps, additionalProps);
27
27
  }
28
28
  get toolName() {
29
29
  return this.getToolName();
@@ -43,7 +43,7 @@ class BaseTool {
43
43
  return strategies[activeStrategy][callbackType]?.call(this, enabledElement, operationData, ...extraArgs);
44
44
  }
45
45
  setConfiguration(newConfiguration) {
46
- this.configuration = utilities.deepMerge(this.configuration, newConfiguration);
46
+ this.configuration = csUtils.deepMerge(this.configuration, newConfiguration);
47
47
  }
48
48
  setActiveStrategy(strategyName) {
49
49
  this.setConfiguration({ activeStrategy: strategyName });
@@ -51,8 +51,8 @@ class BaseTool {
51
51
  getTargetImageData(targetId) {
52
52
  if (targetId.startsWith('imageId:')) {
53
53
  const imageId = targetId.split('imageId:')[1];
54
- const imageURI = utilities.imageIdToURI(imageId);
55
- let viewports = utilities.getViewportsWithImageURI(imageURI);
54
+ const imageURI = csUtils.imageIdToURI(imageId);
55
+ let viewports = csUtils.getViewportsWithImageURI(imageURI);
56
56
  if (!viewports || !viewports.length) {
57
57
  return;
58
58
  }
@@ -65,16 +65,16 @@ class BaseTool {
65
65
  return viewports[0].getImageData();
66
66
  }
67
67
  else if (targetId.startsWith('volumeId:')) {
68
- const volumeId = utilities.getVolumeId(targetId);
69
- const viewports = utilities.getViewportsWithVolumeId(volumeId);
68
+ const volumeId = csUtils.getVolumeId(targetId);
69
+ const viewports = csUtils.getViewportsWithVolumeId(volumeId);
70
70
  if (!viewports || !viewports.length) {
71
71
  return;
72
72
  }
73
73
  return viewports[0].getImageData();
74
74
  }
75
75
  else if (targetId.startsWith('videoId:')) {
76
- const imageURI = utilities.imageIdToURI(targetId);
77
- const viewports = utilities.getViewportsWithImageURI(imageURI);
76
+ const imageURI = csUtils.imageIdToURI(targetId);
77
+ const viewports = csUtils.getViewportsWithImageURI(imageURI);
78
78
  if (!viewports || !viewports.length) {
79
79
  return;
80
80
  }
@@ -129,6 +129,33 @@ class BaseTool {
129
129
  static endGroupRecording() {
130
130
  DefaultHistoryMemo.endGroupRecording();
131
131
  }
132
+ static calculateLengthInIndex(calibrate, indexPoints, closed = false) {
133
+ const scale = calibrate?.scale || 1;
134
+ const scaleY = calibrate?.scaleY || scale;
135
+ const scaleZ = calibrate?.scaleZ || scale;
136
+ let length = 0;
137
+ const count = indexPoints.length;
138
+ const start = closed ? 0 : 1;
139
+ let lastPoint = closed ? indexPoints[count - 1] : indexPoints[0];
140
+ for (let i = start; i < count; i++) {
141
+ const point = indexPoints[i];
142
+ const dx = (point[0] - lastPoint[0]) / scale;
143
+ const dy = (point[1] - lastPoint[1]) / scaleY;
144
+ const dz = (point[2] - lastPoint[2]) / scaleZ;
145
+ length += Math.sqrt(dx * dx + dy * dy + dz * dz);
146
+ lastPoint = point;
147
+ }
148
+ return length;
149
+ }
150
+ static isInsideVolume(dimensions, indexPoints) {
151
+ const { length: count } = indexPoints;
152
+ for (let i = 0; i < count; i++) {
153
+ if (!csUtils.indexWithinDimensions(indexPoints[i], dimensions)) {
154
+ return false;
155
+ }
156
+ }
157
+ return true;
158
+ }
132
159
  }
133
160
  BaseTool.toolName = 'BaseTool';
134
161
  export default BaseTool;
@@ -418,7 +418,10 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
418
418
  worldPos2Index[2] = Math.floor(worldPos2Index[2]);
419
419
  worldPos2Index[indexOfProjection] =
420
420
  worldProjectionPointIndex[indexOfProjection];
421
- if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
421
+ if (CircleROITool.isInsideVolume(dimensions, [
422
+ worldPos1Index,
423
+ worldPos2Index,
424
+ ])) {
422
425
  const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
423
426
  const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
424
427
  const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
@@ -1,13 +1,15 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
- type Statistics = {
2
+ import type { MeasurementType } from '../enums/MeasurementType';
3
+ export type Statistics = {
3
4
  name: string;
4
5
  label?: string;
5
6
  value: number | number[];
6
7
  unit: null | string;
7
8
  pointIJK?: Types.Point3;
8
9
  pointLPS?: Types.Point3;
10
+ type?: MeasurementType;
9
11
  };
10
- type NamedStatistics = {
12
+ export type NamedStatistics = {
11
13
  mean: Statistics & {
12
14
  name: 'mean';
13
15
  };
@@ -60,4 +62,3 @@ type NamedStatistics = {
60
62
  }>;
61
63
  array: Statistics[];
62
64
  };
63
- export type { Statistics, NamedStatistics };
@@ -10,6 +10,5 @@ import getContourHolesDataCanvas from './getContourHolesDataCanvas';
10
10
  import updateContourPolyline from './updateContourPolyline';
11
11
  import acceptAutogeneratedInterpolations from './interpolation/acceptAutogeneratedInterpolations';
12
12
  import findHandlePolylineIndex from './findHandlePolylineIndex';
13
- import calculatePerimeter from './calculatePerimeter';
14
13
  import findIslands from './findIslands';
15
- export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, calculatePerimeter, findIslands, };
14
+ export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, findIslands, };
@@ -10,6 +10,5 @@ import getContourHolesDataCanvas from './getContourHolesDataCanvas';
10
10
  import updateContourPolyline from './updateContourPolyline';
11
11
  import acceptAutogeneratedInterpolations from './interpolation/acceptAutogeneratedInterpolations';
12
12
  import findHandlePolylineIndex from './findHandlePolylineIndex';
13
- import calculatePerimeter from './calculatePerimeter';
14
13
  import findIslands from './findIslands';
15
- export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, calculatePerimeter, findIslands, };
14
+ export { areCoplanarContours, contourFinder, getDeduplicatedVTKPolyDataPoints, detectContourHoles, findContourHoles, generateContourSetsFromLabelmap, AnnotationToPointData, getContourHolesDataWorld, getContourHolesDataCanvas, updateContourPolyline, acceptAutogeneratedInterpolations, findHandlePolylineIndex, findIslands, };
@@ -2,6 +2,8 @@ declare const getCalibratedLengthUnitsAndScale: (image: any, handles: any) => {
2
2
  unit: string;
3
3
  areaUnit: string;
4
4
  scale: number;
5
+ scaleY: number;
6
+ scaleZ: number;
5
7
  volumeUnit: string;
6
8
  };
7
9
  declare const getCalibratedProbeUnitsAndValue: (image: any, handles: any) => {
@@ -1,4 +1,4 @@
1
- import { Enums, utilities } from '@cornerstonejs/core';
1
+ import { Enums } from '@cornerstonejs/core';
2
2
  const { CalibrationTypes } = Enums;
3
3
  const PIXEL_UNITS = 'px';
4
4
  const VOXEL_UNITS = 'voxels';
@@ -8,10 +8,6 @@ const SUPPORTED_REGION_DATA_TYPES = [
8
8
  3,
9
9
  4,
10
10
  ];
11
- const SUPPORTED_LENGTH_VARIANT = [
12
- '3,3',
13
- '4,7',
14
- ];
15
11
  const SUPPORTED_PROBE_VARIANT = [
16
12
  '4,3',
17
13
  '4,7',
@@ -31,94 +27,67 @@ const UNIT_MAPPING = {
31
27
  };
32
28
  const EPS = 1e-3;
33
29
  const SQUARE = '\xb2';
30
+ const types = [
31
+ CalibrationTypes.ERMF,
32
+ CalibrationTypes.USER,
33
+ CalibrationTypes.ERROR,
34
+ CalibrationTypes.PROJECTION,
35
+ CalibrationTypes.CALIBRATED,
36
+ CalibrationTypes.UNKNOWN,
37
+ ];
34
38
  const getCalibratedLengthUnitsAndScale = (image, handles) => {
35
- const { calibration, hasPixelSpacing } = image;
39
+ const { calibration, hasPixelSpacing, spacing = [1, 1, 1] } = image;
36
40
  let unit = hasPixelSpacing ? 'mm' : PIXEL_UNITS;
37
41
  const volumeUnit = hasPixelSpacing ? 'mm\xb3' : VOXEL_UNITS;
38
42
  let areaUnit = unit + SQUARE;
39
- let scale = 1;
43
+ const baseScale = calibration?.scale || 1;
44
+ let scale = baseScale / (calibration?.columnPixelSpacing || spacing[0]);
45
+ let scaleY = baseScale / (calibration?.rowPixelSpacing || spacing[1]);
46
+ let scaleZ = baseScale / spacing[2];
40
47
  let calibrationType = '';
41
48
  if (!calibration ||
42
49
  (!calibration.type && !calibration.sequenceOfUltrasoundRegions)) {
43
- return { unit, areaUnit, scale, volumeUnit };
50
+ return { unit, areaUnit, scale, scaleY, scaleZ, volumeUnit };
51
+ }
52
+ if (types.includes(calibration?.type)) {
53
+ calibrationType = calibration.type;
44
54
  }
45
55
  if (calibration.type === CalibrationTypes.UNCALIBRATED) {
46
56
  return {
47
57
  unit: PIXEL_UNITS,
48
58
  areaUnit: PIXEL_UNITS + SQUARE,
49
59
  scale,
60
+ scaleY,
61
+ scaleZ,
50
62
  volumeUnit: VOXEL_UNITS,
51
63
  };
52
64
  }
53
65
  if (calibration.sequenceOfUltrasoundRegions) {
54
- let imageIndex1, imageIndex2;
55
- if (Array.isArray(handles) && handles.length === 2) {
56
- [imageIndex1, imageIndex2] = handles;
57
- }
58
- else if (typeof handles === 'function') {
59
- const points = handles();
60
- imageIndex1 = points[0];
61
- imageIndex2 = points[1];
62
- }
63
- let regions = calibration.sequenceOfUltrasoundRegions.filter((region) => imageIndex1[0] >= region.regionLocationMinX0 &&
64
- imageIndex1[0] <= region.regionLocationMaxX1 &&
65
- imageIndex1[1] >= region.regionLocationMinY0 &&
66
- imageIndex1[1] <= region.regionLocationMaxY1 &&
67
- imageIndex2[0] >= region.regionLocationMinX0 &&
68
- imageIndex2[0] <= region.regionLocationMaxX1 &&
69
- imageIndex2[1] >= region.regionLocationMinY0 &&
70
- imageIndex2[1] <= region.regionLocationMaxY1);
71
- if (!regions?.length) {
72
- return { unit, areaUnit, scale, volumeUnit };
73
- }
74
- regions = regions.filter((region) => SUPPORTED_REGION_DATA_TYPES.includes(region.regionDataType) &&
75
- SUPPORTED_LENGTH_VARIANT.includes(`${region.physicalUnitsXDirection},${region.physicalUnitsYDirection}`));
76
- if (!regions.length) {
77
- return {
78
- unit: PIXEL_UNITS,
79
- areaUnit: PIXEL_UNITS + SQUARE,
80
- scale,
81
- volumeUnit: VOXEL_UNITS,
82
- };
83
- }
84
- const region = regions[0];
85
- const physicalDeltaX = Math.abs(region.physicalDeltaX);
86
- const physicalDeltaY = Math.abs(region.physicalDeltaY);
87
- const isSamePhysicalDelta = utilities.isEqual(physicalDeltaX, physicalDeltaY, EPS);
88
- if (isSamePhysicalDelta) {
66
+ const region = calibration.sequenceOfUltrasoundRegions.find((region) => handles.every((handle) => handle[0] >= region.regionLocationMinX0 &&
67
+ handle[0] <= region.regionLocationMaxX1 &&
68
+ handle[1] >= region.regionLocationMinY0 &&
69
+ handle[1] <= region.regionLocationMaxY1) && SUPPORTED_REGION_DATA_TYPES.includes(region.regionDataType));
70
+ if (region &&
71
+ region.physicalUnitsXDirection === region.physicalUnitsYDirection) {
72
+ const physicalDeltaX = Math.abs(region.physicalDeltaX);
73
+ const physicalDeltaY = Math.abs(region.physicalDeltaY);
89
74
  scale = 1 / physicalDeltaX;
75
+ scaleY = 1 / physicalDeltaY;
90
76
  calibrationType = 'US Region';
91
77
  unit = UNIT_MAPPING[region.physicalUnitsXDirection] || 'unknown';
92
78
  areaUnit = unit + SQUARE;
93
79
  }
94
- else {
95
- return {
96
- unit: PIXEL_UNITS,
97
- areaUnit: PIXEL_UNITS + SQUARE,
98
- scale,
99
- volumeUnit: VOXEL_UNITS,
100
- };
101
- }
102
80
  }
103
81
  else if (calibration.scale) {
104
82
  scale = calibration.scale;
105
83
  }
106
- const types = [
107
- CalibrationTypes.ERMF,
108
- CalibrationTypes.USER,
109
- CalibrationTypes.ERROR,
110
- CalibrationTypes.PROJECTION,
111
- CalibrationTypes.CALIBRATED,
112
- CalibrationTypes.UNKNOWN,
113
- ];
114
- if (types.includes(calibration?.type)) {
115
- calibrationType = calibration.type;
116
- }
117
84
  return {
118
85
  unit: unit + (calibrationType ? ` ${calibrationType}` : ''),
119
86
  areaUnit: areaUnit + (calibrationType ? ` ${calibrationType}` : ''),
120
- scale,
121
87
  volumeUnit: volumeUnit + (calibrationType ? ` ${calibrationType}` : ''),
88
+ scale,
89
+ scaleY,
90
+ scaleZ,
122
91
  };
123
92
  };
124
93
  const getCalibratedProbeUnitsAndValue = (image, handles) => {
@@ -1 +1 @@
1
- export declare const version = "4.12.4";
1
+ export declare const version = "4.12.6";
@@ -1 +1 @@
1
- export const version = '4.12.4';
1
+ export const version = '4.12.6';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "4.12.4",
3
+ "version": "4.12.6",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "types": "./dist/esm/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -108,7 +108,7 @@
108
108
  "canvas": "3.2.0"
109
109
  },
110
110
  "peerDependencies": {
111
- "@cornerstonejs/core": "4.12.4",
111
+ "@cornerstonejs/core": "4.12.6",
112
112
  "@kitware/vtk.js": "34.15.1",
113
113
  "@types/d3-array": "3.2.1",
114
114
  "@types/d3-interpolate": "3.0.4",
@@ -127,5 +127,5 @@
127
127
  "type": "individual",
128
128
  "url": "https://ohif.org/donate"
129
129
  },
130
- "gitHead": "709d5a61b1acccf6a2dd8f9193907f98b5ed6c4c"
130
+ "gitHead": "67da5ec35cb711e4310f0f1971bd8ed090377338"
131
131
  }
@@ -1,2 +0,0 @@
1
- export declare function calculatePerimeter(polyline: number[][], closed: boolean): number;
2
- export default calculatePerimeter;
@@ -1,16 +0,0 @@
1
- import { vec3 } from 'gl-matrix';
2
- export function calculatePerimeter(polyline, closed) {
3
- let perimeter = 0;
4
- for (let i = 0; i < polyline.length - 1; i++) {
5
- const point1 = polyline[i];
6
- const point2 = polyline[i + 1];
7
- perimeter += vec3.dist(point1, point2);
8
- }
9
- if (closed) {
10
- const firstPoint = polyline[0];
11
- const lastPoint = polyline[polyline.length - 1];
12
- perimeter += vec3.dist(firstPoint, lastPoint);
13
- }
14
- return perimeter;
15
- }
16
- export default calculatePerimeter;