@cornerstonejs/tools 2.0.0-beta.22 → 2.0.0-beta.24

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 (47) hide show
  1. package/dist/esm/eventListeners/segmentation/labelmap/onLabelmapSegmentationDataModified.js +27 -23
  2. package/dist/esm/eventListeners/segmentation/segmentationDataModifiedEventListener.js +2 -2
  3. package/dist/esm/index.d.ts +2 -2
  4. package/dist/esm/index.js +2 -2
  5. package/dist/esm/stateManagement/index.d.ts +2 -0
  6. package/dist/esm/stateManagement/index.js +2 -2
  7. package/dist/esm/stateManagement/segmentation/SegmentationRenderingEngine.d.ts +1 -2
  8. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.d.ts +3 -0
  9. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.js +59 -11
  10. package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.d.ts +1 -0
  11. package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.js +5 -0
  12. package/dist/esm/stateManagement/segmentation/segmentationState.d.ts +2 -1
  13. package/dist/esm/stateManagement/segmentation/segmentationState.js +2 -1
  14. package/dist/esm/tools/annotation/BidirectionalTool.js +6 -6
  15. package/dist/esm/tools/annotation/CircleROITool.js +12 -12
  16. package/dist/esm/tools/annotation/DragProbeTool.js +2 -2
  17. package/dist/esm/tools/annotation/EllipticalROITool.js +11 -11
  18. package/dist/esm/tools/annotation/HeightTool.js +4 -4
  19. package/dist/esm/tools/annotation/LengthTool.js +4 -4
  20. package/dist/esm/tools/annotation/PlanarFreehandROITool.d.ts +4 -4
  21. package/dist/esm/tools/annotation/PlanarFreehandROITool.js +19 -19
  22. package/dist/esm/tools/annotation/ProbeTool.js +8 -8
  23. package/dist/esm/tools/annotation/RectangleROITool.js +11 -11
  24. package/dist/esm/tools/annotation/SplineROITool.js +6 -6
  25. package/dist/esm/tools/index.d.ts +1 -3
  26. package/dist/esm/tools/index.js +1 -3
  27. package/dist/esm/tools/segmentation/BrushTool.d.ts +38 -0
  28. package/dist/esm/tools/segmentation/BrushTool.js +40 -7
  29. package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +8 -8
  30. package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +8 -8
  31. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +10 -5
  32. package/dist/esm/tools/segmentation/strategies/compositions/islandRemoval.js +1 -1
  33. package/dist/esm/tools/segmentation/strategies/compositions/preview.js +2 -2
  34. package/dist/esm/tools/segmentation/strategies/compositions/setValue.js +6 -4
  35. package/dist/esm/tools/segmentation/strategies/fillSphere.js +5 -14
  36. package/dist/esm/types/LabelmapToolOperationData.d.ts +5 -0
  37. package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +2 -2
  38. package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +7 -7
  39. package/dist/esm/utilities/getCalibratedUnits.d.ts +2 -2
  40. package/dist/esm/utilities/getCalibratedUnits.js +13 -13
  41. package/dist/esm/utilities/segmentation/getSegmentIndexAtLabelmapBorder.js +7 -3
  42. package/dist/esm/utilities/segmentation/isLineInSegment.js +3 -3
  43. package/dist/umd/index.js +1 -1
  44. package/dist/umd/index.js.map +1 -1
  45. package/package.json +3 -3
  46. package/dist/esm/tools/StackScrollToolMouseWheelTool.d.ts +0 -16
  47. package/dist/esm/tools/StackScrollToolMouseWheelTool.js +0 -33
@@ -148,7 +148,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
148
148
  isPreScaled: isViewportPreScaled(viewport, targetId),
149
149
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
150
150
  };
151
- const pixelValueUnits = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
151
+ const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
152
152
  const calibratedScale = getCalibratedLengthUnitsAndScale(image, () => {
153
153
  const polyline = data.contour.polyline;
154
154
  const numPoints = polyline.length;
@@ -175,7 +175,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
175
175
  imageData,
176
176
  metadata,
177
177
  cachedStats,
178
- pixelValueUnits,
178
+ modalityUnit,
179
179
  calibratedScale,
180
180
  });
181
181
  }
@@ -185,7 +185,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
185
185
  canvasCoordinates,
186
186
  targetId,
187
187
  cachedStats,
188
- pixelValueUnits,
188
+ modalityUnit,
189
189
  calibratedScale,
190
190
  });
191
191
  }
@@ -365,14 +365,14 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
365
365
  if (!this.commonData?.movingTextBox) {
366
366
  const { data } = annotation;
367
367
  if (!data.cachedStats[targetId] ||
368
- data.cachedStats[targetId].areaUnits == null) {
368
+ data.cachedStats[targetId].areaUnit == null) {
369
369
  data.cachedStats[targetId] = {
370
370
  Modality: null,
371
371
  area: null,
372
372
  max: null,
373
373
  mean: null,
374
374
  stdDev: null,
375
- areaUnits: null,
375
+ areaUnit: null,
376
376
  };
377
377
  this._calculateCachedStats(annotation, viewport, renderingEngine, enabledElement);
378
378
  }
@@ -381,8 +381,8 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
381
381
  }
382
382
  }
383
383
  }
384
- updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId, pixelValueUnits, canvasCoordinates, calibratedScale, }) {
385
- const { scale, areaUnits, units } = calibratedScale;
384
+ updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId, modalityUnit, canvasCoordinates, calibratedScale, }) {
385
+ const { scale, areaUnit, units } = calibratedScale;
386
386
  const { voxelManager } = viewport.getImageData();
387
387
  const canvasPoint = canvasCoordinates[0];
388
388
  const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
@@ -484,45 +484,45 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
484
484
  stdDev: stats.stdDev?.value,
485
485
  statsArray: stats.array,
486
486
  pointsInShape: pointsInShape,
487
- areaUnits,
488
- pixelValueUnits,
489
- lengthUnits: units,
487
+ areaUnit,
488
+ modalityUnit,
489
+ unit: units,
490
490
  };
491
491
  }
492
- updateOpenCachedStats({ targetId, metadata, canvasCoordinates, cachedStats, pixelValueUnits, calibratedScale, }) {
492
+ updateOpenCachedStats({ targetId, metadata, canvasCoordinates, cachedStats, modalityUnit, calibratedScale, }) {
493
493
  const { scale, units } = calibratedScale;
494
494
  cachedStats[targetId] = {
495
495
  Modality: metadata.Modality,
496
496
  length: calculatePerimeter(canvasCoordinates, false) / scale,
497
- pixelValueUnits,
497
+ modalityUnit,
498
498
  getPixelValueUnitunit: units,
499
499
  };
500
500
  }
501
501
  }
502
502
  function defaultGetTextLines(data, targetId) {
503
503
  const cachedVolumeStats = data.cachedStats[targetId];
504
- const { area, mean, stdDev, length, perimeter, max, isEmptyArea, lengthUnits, areaUnits, pixelValueUnits, } = cachedVolumeStats || {};
504
+ const { area, mean, stdDev, length, perimeter, max, isEmptyArea, unit, areaUnit, modalityUnit, } = cachedVolumeStats || {};
505
505
  const textLines = [];
506
506
  if (area) {
507
507
  const areaLine = isEmptyArea
508
508
  ? `Area: Oblique not supported`
509
- : `Area: ${csUtils.roundNumber(area)} ${areaUnits}`;
509
+ : `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
510
510
  textLines.push(areaLine);
511
511
  }
512
512
  if (mean) {
513
- textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${pixelValueUnits}`);
513
+ textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
514
514
  }
515
515
  if (Number.isFinite(max)) {
516
- textLines.push(`Max: ${csUtils.roundNumber(max)} ${pixelValueUnits}`);
516
+ textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
517
517
  }
518
518
  if (stdDev) {
519
- textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${pixelValueUnits}`);
519
+ textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
520
520
  }
521
521
  if (perimeter) {
522
- textLines.push(`Perimeter: ${csUtils.roundNumber(perimeter)} ${lengthUnits}`);
522
+ textLines.push(`Perimeter: ${csUtils.roundNumber(perimeter)} ${unit}`);
523
523
  }
524
524
  if (length) {
525
- textLines.push(`${csUtils.roundNumber(length)} ${lengthUnits}`);
525
+ textLines.push(`${csUtils.roundNumber(length)} ${unit}`);
526
526
  }
527
527
  return textLines;
528
528
  }
@@ -278,25 +278,25 @@ class ProbeTool extends AnnotationTool {
278
278
  const viewport = viewports[0];
279
279
  ijk[2] = viewport.getCurrentImageIdIndex();
280
280
  }
281
- let pixelValueUnits;
281
+ let modalityUnit;
282
282
  if (modality === 'US') {
283
283
  const calibratedResults = getCalibratedProbeUnitsAndValue(image, [
284
284
  ijk,
285
285
  ]);
286
286
  const hasEnhancedRegionValues = calibratedResults.values.every((value) => value !== null);
287
287
  value = (hasEnhancedRegionValues ? calibratedResults.values : value);
288
- pixelValueUnits = hasEnhancedRegionValues
288
+ modalityUnit = hasEnhancedRegionValues
289
289
  ? calibratedResults.units
290
290
  : 'raw';
291
291
  }
292
292
  else {
293
- pixelValueUnits = getPixelValueUnits(modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
293
+ modalityUnit = getPixelValueUnits(modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
294
294
  }
295
295
  cachedStats[targetId] = {
296
296
  index: ijk,
297
297
  value,
298
298
  Modality: modality,
299
- pixelValueUnits,
299
+ modalityUnit,
300
300
  };
301
301
  }
302
302
  else {
@@ -314,19 +314,19 @@ class ProbeTool extends AnnotationTool {
314
314
  }
315
315
  function defaultGetTextLines(data, targetId) {
316
316
  const cachedVolumeStats = data.cachedStats[targetId];
317
- const { index, value, pixelValueUnits } = cachedVolumeStats;
317
+ const { index, value, modalityUnit } = cachedVolumeStats;
318
318
  if (value === undefined) {
319
319
  return;
320
320
  }
321
321
  const textLines = [];
322
322
  textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);
323
- if (value instanceof Array && pixelValueUnits instanceof Array) {
323
+ if (value instanceof Array && modalityUnit instanceof Array) {
324
324
  for (let i = 0; i < value.length; i++) {
325
- textLines.push(`${csUtils.roundNumber(value[i])} ${pixelValueUnits[i]}`);
325
+ textLines.push(`${csUtils.roundNumber(value[i])} ${modalityUnit[i]}`);
326
326
  }
327
327
  }
328
328
  else {
329
- textLines.push(`${csUtils.roundNumber(value)} ${pixelValueUnits}`);
329
+ textLines.push(`${csUtils.roundNumber(value)} ${modalityUnit}`);
330
330
  }
331
331
  return textLines;
332
332
  }
@@ -346,14 +346,14 @@ class RectangleROITool extends AnnotationTool {
346
346
  });
347
347
  const { viewPlaneNormal, viewUp } = viewport.getCamera();
348
348
  if (!data.cachedStats[targetId] ||
349
- data.cachedStats[targetId].areaUnits == null) {
349
+ data.cachedStats[targetId].areaUnit == null) {
350
350
  data.cachedStats[targetId] = {
351
351
  Modality: null,
352
352
  area: null,
353
353
  max: null,
354
354
  mean: null,
355
355
  stdDev: null,
356
- areaUnits: null,
356
+ areaUnit: null,
357
357
  };
358
358
  this._calculateCachedStats(annotation, viewPlaneNormal, viewUp, renderingEngine, enabledElement);
359
359
  }
@@ -487,13 +487,13 @@ class RectangleROITool extends AnnotationTool {
487
487
  ];
488
488
  const { worldWidth, worldHeight } = getWorldWidthAndHeightFromCorners(viewPlaneNormal, viewUp, worldPos1, worldPos2);
489
489
  const handles = [pos1Index, pos2Index];
490
- const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(image, handles);
490
+ const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
491
491
  const area = Math.abs(worldWidth * worldHeight) / (scale * scale);
492
492
  const pixelUnitsOptions = {
493
493
  isPreScaled: isViewportPreScaled(viewport, targetId),
494
494
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
495
495
  };
496
- const pixelValueUnits = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
496
+ const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
497
497
  const pointsInShape = voxelManager.forEach(this.configuration.statsCalculator.statsCallback, {
498
498
  boundsIJK,
499
499
  imageData,
@@ -508,8 +508,8 @@ class RectangleROITool extends AnnotationTool {
508
508
  max: stats.max?.value,
509
509
  statsArray: stats.array,
510
510
  pointsInShape: pointsInShape,
511
- areaUnits,
512
- pixelValueUnits,
511
+ areaUnit,
512
+ modalityUnit,
513
513
  };
514
514
  }
515
515
  else {
@@ -532,15 +532,15 @@ class RectangleROITool extends AnnotationTool {
532
532
  }
533
533
  function defaultGetTextLines(data, targetId) {
534
534
  const cachedVolumeStats = data.cachedStats[targetId];
535
- const { area, mean, max, stdDev, areaUnits, pixelValueUnits } = cachedVolumeStats;
535
+ const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
536
536
  if (mean === undefined) {
537
537
  return;
538
538
  }
539
539
  const textLines = [];
540
- textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnits}`);
541
- textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${pixelValueUnits}`);
542
- textLines.push(`Max: ${csUtils.roundNumber(max)} ${pixelValueUnits}`);
543
- textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${pixelValueUnits}`);
540
+ textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
541
+ textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
542
+ textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
543
+ textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
544
544
  return textLines;
545
545
  }
546
546
  RectangleROITool.toolName = 'RectangleROI';
@@ -452,7 +452,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
452
452
  const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
453
453
  const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
454
454
  const { imageData } = image;
455
- const { scale, areaUnits } = getCalibratedLengthUnitsAndScale(image, () => {
455
+ const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, () => {
456
456
  const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(canvasCoordinates);
457
457
  const topLeftBBWorld = viewport.canvasToWorld([
458
458
  canvasMinX,
@@ -471,7 +471,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
471
471
  cachedStats[targetId] = {
472
472
  Modality: metadata.Modality,
473
473
  area,
474
- areaUnits,
474
+ areaUnit,
475
475
  };
476
476
  }
477
477
  this.triggerAnnotationModified(annotation, enabledElement, ChangeTypes.StatsUpdated);
@@ -560,11 +560,11 @@ class SplineROITool extends ContourSegmentationBaseTool {
560
560
  });
561
561
  super.renderAnnotationInstance(renderContext);
562
562
  if (!data.cachedStats[targetId] ||
563
- data.cachedStats[targetId].areaUnits == null) {
563
+ data.cachedStats[targetId].areaUnit == null) {
564
564
  data.cachedStats[targetId] = {
565
565
  Modality: null,
566
566
  area: null,
567
- areaUnits: null,
567
+ areaUnit: null,
568
568
  };
569
569
  this._calculateCachedStats(annotation, element);
570
570
  }
@@ -715,12 +715,12 @@ class SplineROITool extends ContourSegmentationBaseTool {
715
715
  }
716
716
  function defaultGetTextLines(data, targetId) {
717
717
  const cachedVolumeStats = data.cachedStats[targetId];
718
- const { area, isEmptyArea, areaUnits } = cachedVolumeStats;
718
+ const { area, isEmptyArea, areaUnit } = cachedVolumeStats;
719
719
  const textLines = [];
720
720
  if (area) {
721
721
  const areaLine = isEmptyArea
722
722
  ? `Area: Oblique not supported`
723
- : `Area: ${utilities.roundNumber(area)} ${areaUnits}`;
723
+ : `Area: ${utilities.roundNumber(area)} ${areaUnit}`;
724
724
  textLines.push(areaLine);
725
725
  }
726
726
  return textLines;
@@ -5,7 +5,6 @@ import WindowLevelTool from './WindowLevelTool';
5
5
  import WindowLevelRegionTool from './WindowLevelRegionTool';
6
6
  import StackScrollTool from './StackScrollTool';
7
7
  import PlanarRotateTool from './PlanarRotateTool';
8
- import StackScrollMouseWheelTool from './StackScrollToolMouseWheelTool';
9
8
  import ZoomTool from './ZoomTool';
10
9
  import VolumeRotateMouseWheelTool from './VolumeRotateMouseWheelTool';
11
10
  import MIPJumpToClickTool from './MIPJumpToClickTool';
@@ -16,7 +15,6 @@ import ReferenceLinesTool from './ReferenceLinesTool';
16
15
  import OverlayGridTool from './OverlayGridTool';
17
16
  import SegmentationIntersectionTool from './SegmentationIntersectionTool';
18
17
  import ReferenceCursors from './ReferenceCursors';
19
- import ReferenceLines from './ReferenceLinesTool';
20
18
  import ScaleOverlayTool from './ScaleOverlayTool';
21
19
  import SculptorTool from './SculptorTool';
22
20
  import BidirectionalTool from './annotation/BidirectionalTool';
@@ -49,4 +47,4 @@ import BrushTool from './segmentation/BrushTool';
49
47
  import PaintFillTool from './segmentation/PaintFillTool';
50
48
  import OrientationMarkerTool from './OrientationMarkerTool';
51
49
  import SegmentSelectTool from './segmentation/SegmentSelectTool';
52
- export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, StackScrollMouseWheelTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, ReferenceLines, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
50
+ export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
@@ -5,7 +5,6 @@ import WindowLevelTool from './WindowLevelTool';
5
5
  import WindowLevelRegionTool from './WindowLevelRegionTool';
6
6
  import StackScrollTool from './StackScrollTool';
7
7
  import PlanarRotateTool from './PlanarRotateTool';
8
- import StackScrollMouseWheelTool from './StackScrollToolMouseWheelTool';
9
8
  import ZoomTool from './ZoomTool';
10
9
  import VolumeRotateMouseWheelTool from './VolumeRotateMouseWheelTool';
11
10
  import MIPJumpToClickTool from './MIPJumpToClickTool';
@@ -16,7 +15,6 @@ import ReferenceLinesTool from './ReferenceLinesTool';
16
15
  import OverlayGridTool from './OverlayGridTool';
17
16
  import SegmentationIntersectionTool from './SegmentationIntersectionTool';
18
17
  import ReferenceCursors from './ReferenceCursors';
19
- import ReferenceLines from './ReferenceLinesTool';
20
18
  import ScaleOverlayTool from './ScaleOverlayTool';
21
19
  import SculptorTool from './SculptorTool';
22
20
  import BidirectionalTool from './annotation/BidirectionalTool';
@@ -49,4 +47,4 @@ import BrushTool from './segmentation/BrushTool';
49
47
  import PaintFillTool from './segmentation/PaintFillTool';
50
48
  import OrientationMarkerTool from './OrientationMarkerTool';
51
49
  import SegmentSelectTool from './segmentation/SegmentSelectTool';
52
- export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, StackScrollMouseWheelTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, ReferenceLines, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
50
+ export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
@@ -1,6 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import type { PublicToolProps, ToolProps, EventTypes, SVGDrawingHelper } from '../../types';
3
3
  import { BaseTool } from '../base';
4
+ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
4
5
  export type PreviewData = {
5
6
  preview: unknown;
6
7
  timer?: number;
@@ -24,11 +25,22 @@ declare class BrushTool extends BaseTool {
24
25
  referencedVolumeId: any;
25
26
  segmentsLocked: number[] | [];
26
27
  imageId?: undefined;
28
+ override?: undefined;
27
29
  } | {
28
30
  imageId: string;
29
31
  segmentsLocked: number[] | [];
30
32
  volumeId?: undefined;
31
33
  referencedVolumeId?: undefined;
34
+ override?: undefined;
35
+ } | {
36
+ imageId: string;
37
+ segmentsLocked: number[] | [];
38
+ override: {
39
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
40
+ imageData: vtkImageData;
41
+ };
42
+ volumeId?: undefined;
43
+ referencedVolumeId?: undefined;
32
44
  };
33
45
  preMouseDownCallback: (evt: EventTypes.MouseDownActivateEventType) => boolean;
34
46
  mouseMoveCallback: (evt: EventTypes.InteractionEventType) => void;
@@ -48,8 +60,13 @@ declare class BrushTool extends BaseTool {
48
60
  viewUp: any;
49
61
  strategySpecificConfiguration: any;
50
62
  preview: unknown;
63
+ override: {
64
+ voxelManager: Types.IVoxelManager<number>;
65
+ imageData: vtkImageData;
66
+ };
51
67
  segmentsLocked: number[];
52
68
  imageId?: string;
69
+ imageIds?: string[];
53
70
  volumeId?: string;
54
71
  referencedVolumeId?: string;
55
72
  } | {
@@ -67,6 +84,23 @@ declare class BrushTool extends BaseTool {
67
84
  referencedVolumeId: any;
68
85
  segmentsLocked: number[] | [];
69
86
  imageId?: undefined;
87
+ override?: undefined;
88
+ } | {
89
+ points: any;
90
+ segmentIndex: number;
91
+ previewColors: any;
92
+ viewPlaneNormal: any;
93
+ toolGroupId: string;
94
+ segmentationId: string;
95
+ segmentationRepresentationUID: string;
96
+ viewUp: any;
97
+ strategySpecificConfiguration: any;
98
+ preview: unknown;
99
+ imageId: string;
100
+ segmentsLocked: number[] | [];
101
+ volumeId?: undefined;
102
+ referencedVolumeId?: undefined;
103
+ override?: undefined;
70
104
  } | {
71
105
  points: any;
72
106
  segmentIndex: number;
@@ -80,6 +114,10 @@ declare class BrushTool extends BaseTool {
80
114
  preview: unknown;
81
115
  imageId: string;
82
116
  segmentsLocked: number[] | [];
117
+ override: {
118
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
119
+ imageData: vtkImageData;
120
+ };
83
121
  volumeId?: undefined;
84
122
  referencedVolumeId?: undefined;
85
123
  };
@@ -10,10 +10,11 @@ import { drawCircle as drawCircleSvg } from '../../drawingSvg';
10
10
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
11
11
  import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
12
12
  import { isVolumeSegmentation } from './strategies/utils/stackVolumeCheck';
13
- import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, } from '../../stateManagement/segmentation/segmentationState';
13
+ import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, getStackSegmentationImageIdsForViewport, } from '../../stateManagement/segmentation/segmentationState';
14
14
  import { getLockedSegmentIndices } from '../../stateManagement/segmentation/segmentLocking';
15
15
  import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
16
16
  import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
17
+ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
17
18
  class BrushTool extends BaseTool {
18
19
  constructor(toolProps = {}, defaultToolProps = {
19
20
  supportedInteractionTypes: ['Mouse', 'Touch'],
@@ -141,7 +142,6 @@ class BrushTool extends BaseTool {
141
142
  const eventData = evt.detail;
142
143
  const { element, currentPoints } = eventData;
143
144
  const enabledElement = getEnabledElement(element);
144
- const { renderingEngine } = enabledElement;
145
145
  this.updateCursor(evt);
146
146
  const { viewportIdsToRender } = this._hoverData;
147
147
  triggerAnnotationRenderForViewportUIDs(viewportIdsToRender);
@@ -235,12 +235,45 @@ class BrushTool extends BaseTool {
235
235
  return;
236
236
  }
237
237
  if (this.configuration.activeStrategy.includes('SPHERE')) {
238
- throw new Error('Sphere manipulation is not supported for stacks of image segmentations yet');
238
+ const referencedImageIds = viewport.getImageIds();
239
+ const isValidVolumeForSphere = csUtils.isValidVolume(referencedImageIds);
240
+ if (!isValidVolumeForSphere) {
241
+ throw new Error('Volume is not reconstructable for sphere manipulation');
242
+ }
243
+ const labelmapImageIds = getStackSegmentationImageIdsForViewport(viewport.id, segmentationId);
244
+ if (!labelmapImageIds || labelmapImageIds.length === 1) {
245
+ return {
246
+ imageId: segmentationImageId,
247
+ segmentsLocked,
248
+ };
249
+ }
250
+ const tempVolumeId = 'tempVolumeId';
251
+ const { dimensions, direction, origin, spacing, numberOfComponents, imageIds: sortedLabelmapImageIds, } = csUtils.generateVolumePropsFromImageIds(labelmapImageIds, tempVolumeId);
252
+ const newVoxelManager = csUtils.VoxelManager.createImageVolumeVoxelManager({
253
+ dimensions,
254
+ imageIds: sortedLabelmapImageIds,
255
+ numberOfComponents,
256
+ });
257
+ const newImageData = vtkImageData.newInstance();
258
+ newImageData.setDimensions(dimensions);
259
+ newImageData.setSpacing(spacing);
260
+ newImageData.setDirection(direction);
261
+ newImageData.setOrigin(origin);
262
+ return {
263
+ imageId: segmentationImageId,
264
+ segmentsLocked,
265
+ override: {
266
+ voxelManager: newVoxelManager,
267
+ imageData: newImageData,
268
+ },
269
+ };
270
+ }
271
+ else {
272
+ return {
273
+ imageId: segmentationImageId,
274
+ segmentsLocked,
275
+ };
239
276
  }
240
- return {
241
- imageId: segmentationImageId,
242
- segmentsLocked,
243
- };
244
277
  }
245
278
  }
246
279
  createHoverData(element, centerCanvas) {
@@ -345,7 +345,7 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
345
345
  isPreScaled: isViewportPreScaled(viewport, targetId),
346
346
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
347
347
  };
348
- const pixelValueUnits = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
348
+ const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
349
349
  for (let i = 0; i < projectionPoints.length; i++) {
350
350
  if (!imageVolume) {
351
351
  continue;
@@ -409,8 +409,8 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
409
409
  stdDev: stats.stdDev?.value,
410
410
  max: stats.max?.value,
411
411
  statsArray: stats.array,
412
- areaUnits: measureInfo.areaUnits,
413
- pixelValueUnits,
412
+ areaUnit: measureInfo.areaUnit,
413
+ modalityUnit,
414
414
  };
415
415
  }
416
416
  _calculateCachedStatsTool(annotation, enabledElement) {
@@ -456,15 +456,15 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
456
456
  }
457
457
  function defaultGetTextLines(data) {
458
458
  const cachedVolumeStats = data.cachedStats.statistics;
459
- const { area, mean, max, stdDev, areaUnits, pixelValueUnits } = cachedVolumeStats;
459
+ const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
460
460
  if (mean === undefined) {
461
461
  return;
462
462
  }
463
463
  const textLines = [];
464
- textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnits}`);
465
- textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${pixelValueUnits}`);
466
- textLines.push(`Max: ${csUtils.roundNumber(max)} ${pixelValueUnits}`);
467
- textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${pixelValueUnits}`);
464
+ textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
465
+ textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
466
+ textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
467
+ textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
468
468
  return textLines;
469
469
  }
470
470
  CircleROIStartEndThresholdTool.toolName = 'CircleROIStartEndThreshold';
@@ -316,7 +316,7 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
316
316
  isPreScaled: isViewportPreScaled(viewport, targetId),
317
317
  isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
318
318
  };
319
- const pixelValueUnits = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
319
+ const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
320
320
  for (let i = 0; i < projectionPoints.length; i++) {
321
321
  if (!imageVolume) {
322
322
  continue;
@@ -367,8 +367,8 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
367
367
  stdDev: stats.stdDev?.value,
368
368
  max: stats.max?.value,
369
369
  statsArray: stats.array,
370
- areaUnits: measureInfo.areaUnits,
371
- pixelValueUnits,
370
+ areaUnit: measureInfo.areaUnit,
371
+ modalityUnit,
372
372
  };
373
373
  }
374
374
  _calculateCachedStatsTool(annotation, enabledElement) {
@@ -410,15 +410,15 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
410
410
  }
411
411
  function defaultGetTextLines(data) {
412
412
  const cachedVolumeStats = data.cachedStats.statistics;
413
- const { area, mean, max, stdDev, areaUnits, pixelValueUnits } = cachedVolumeStats;
413
+ const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
414
414
  if (mean === undefined) {
415
415
  return;
416
416
  }
417
417
  const textLines = [];
418
- textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnits}`);
419
- textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${pixelValueUnits}`);
420
- textLines.push(`Max: ${csUtils.roundNumber(max)} ${pixelValueUnits}`);
421
- textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${pixelValueUnits}`);
418
+ textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
419
+ textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
420
+ textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
421
+ textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
422
422
  return textLines;
423
423
  }
424
424
  RectangleROIStartEndThresholdTool.toolName = 'RectangleROIStartEndThreshold';
@@ -37,7 +37,8 @@ export default class BrushStrategy {
37
37
  }
38
38
  this._fill.forEach((func) => func(initializedData));
39
39
  const { segmentationVoxelManager, previewVoxelManager, previewSegmentIndex, } = initializedData;
40
- triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfSlices());
40
+ triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices());
41
+ segmentationVoxelManager.resetModifiedSlices();
41
42
  if (!previewSegmentIndex || !previewVoxelManager.modifiedSlices.size) {
42
43
  return null;
43
44
  }
@@ -69,7 +70,9 @@ export default class BrushStrategy {
69
70
  BrushStrategy.childFunctions[key](this, result[key]);
70
71
  }
71
72
  });
72
- this.strategyFunction = (enabledElement, operationData) => this.fill(enabledElement, operationData);
73
+ this.strategyFunction = (enabledElement, operationData) => {
74
+ return this.fill(enabledElement, operationData);
75
+ };
73
76
  for (const key of Object.keys(BrushStrategy.childFunctions)) {
74
77
  this.strategyFunction[key] = this[key];
75
78
  }
@@ -82,9 +85,11 @@ export default class BrushStrategy {
82
85
  return operationData.preview;
83
86
  }
84
87
  const { imageVoxelManager, segmentationVoxelManager, segmentationImageData, } = data;
88
+ const segmentationVoxelManagerToUse = operationData.override?.voxelManager || segmentationVoxelManager;
89
+ const segmentationImageDataToUse = operationData.override?.imageData || segmentationImageData;
85
90
  const previewVoxelManager = operationData.preview?.previewVoxelManager ||
86
91
  VoxelManager.createHistoryVoxelManager({
87
- sourceVoxelManager: segmentationVoxelManager,
92
+ sourceVoxelManager: segmentationVoxelManagerToUse,
88
93
  });
89
94
  const previewEnabled = !!operationData.previewColors;
90
95
  const previewSegmentIndex = previewEnabled ? 255 : undefined;
@@ -94,8 +99,8 @@ export default class BrushStrategy {
94
99
  ...operationData,
95
100
  enabledElement,
96
101
  imageVoxelManager,
97
- segmentationVoxelManager,
98
- segmentationImageData,
102
+ segmentationVoxelManager: segmentationVoxelManagerToUse,
103
+ segmentationImageData: segmentationImageDataToUse,
99
104
  previewVoxelManager,
100
105
  viewport,
101
106
  centerWorld: null,
@@ -118,6 +118,6 @@ export default {
118
118
  }
119
119
  }
120
120
  }
121
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfSlices());
121
+ triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
122
122
  },
123
123
  };
@@ -66,7 +66,7 @@ export default {
66
66
  }
67
67
  };
68
68
  tracking.forEach(callback, {});
69
- triggerSegmentationDataModified(operationData.segmentationId, tracking.getArrayOfSlices());
69
+ triggerSegmentationDataModified(operationData.segmentationId, tracking.getArrayOfModifiedSlices());
70
70
  tracking.clear();
71
71
  },
72
72
  [StrategyCallbacks.RejectPreview]: (operationData) => {
@@ -78,7 +78,7 @@ export default {
78
78
  segmentationVoxelManager.setAtIndex(index, value);
79
79
  };
80
80
  previewVoxelManager.forEach(callback);
81
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfSlices());
81
+ triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
82
82
  previewVoxelManager.clear();
83
83
  },
84
84
  };