@cornerstonejs/tools 5.4.12 → 5.4.14

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 (46) hide show
  1. package/dist/esm/eventListeners/segmentation/segmentationDataModifiedEventListener.js +2 -2
  2. package/dist/esm/index.d.ts +2 -2
  3. package/dist/esm/index.js +2 -2
  4. package/dist/esm/stateManagement/segmentation/SegmentationRenderingEngine.d.ts +2 -1
  5. package/dist/esm/stateManagement/segmentation/SegmentationRenderingEngine.js +45 -1
  6. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapEditTransaction.d.ts +12 -2
  7. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapEditTransaction.js +21 -1
  8. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapImageReferenceResolver.d.ts +5 -0
  9. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapImageReferenceResolver.js +47 -1
  10. package/dist/esm/stateManagement/segmentation/labelmapModel/privateLabelmap.d.ts +5 -1
  11. package/dist/esm/stateManagement/segmentation/labelmapModel/privateLabelmap.js +49 -4
  12. package/dist/esm/tools/annotation/ClickSegmentTool.d.ts +51 -0
  13. package/dist/esm/tools/annotation/ClickSegmentTool.js +748 -0
  14. package/dist/esm/tools/annotation/regionSegmentHoverCursors.d.ts +4 -0
  15. package/dist/esm/tools/annotation/regionSegmentHoverCursors.js +4 -0
  16. package/dist/esm/tools/displayTools/Labelmap/addVolumesAsIndependentComponents.js +42 -11
  17. package/dist/esm/tools/index.d.ts +2 -1
  18. package/dist/esm/tools/index.js +2 -1
  19. package/dist/esm/tools/segmentation/BrushTool.js +1 -0
  20. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +21 -0
  21. package/dist/esm/tools/segmentation/strategies/utils/crossLayerErase.js +20 -0
  22. package/dist/esm/utilities/segmentation/commitSliceMasksToLabelmap.d.ts +12 -0
  23. package/dist/esm/utilities/segmentation/commitSliceMasksToLabelmap.js +128 -0
  24. package/dist/esm/utilities/segmentation/createEnsureSliceLoadedForVolume.d.ts +2 -0
  25. package/dist/esm/utilities/segmentation/createEnsureSliceLoadedForVolume.js +36 -0
  26. package/dist/esm/utilities/segmentation/createLabelmapMemo.d.ts +7 -0
  27. package/dist/esm/utilities/segmentation/createLabelmapMemo.js +41 -8
  28. package/dist/esm/utilities/segmentation/floodFillIslandRemoval.d.ts +20 -0
  29. package/dist/esm/utilities/segmentation/floodFillIslandRemoval.js +118 -0
  30. package/dist/esm/utilities/segmentation/floodFillSliceLazy.d.ts +33 -0
  31. package/dist/esm/utilities/segmentation/floodFillSliceLazy.js +167 -0
  32. package/dist/esm/utilities/segmentation/growCut/floodFillIntensityRangeTypes.d.ts +51 -0
  33. package/dist/esm/utilities/segmentation/growCut/floodFillIntensityRangeTypes.js +0 -0
  34. package/dist/esm/utilities/segmentation/growCut/getViewportVoiMappingForVolume.d.ts +10 -0
  35. package/dist/esm/utilities/segmentation/growCut/getViewportVoiMappingForVolume.js +21 -0
  36. package/dist/esm/utilities/segmentation/growCut/intensityRange/adaptiveRegionIntensityRange.d.ts +57 -0
  37. package/dist/esm/utilities/segmentation/growCut/intensityRange/adaptiveRegionIntensityRange.js +559 -0
  38. package/dist/esm/utilities/segmentation/growCut/neighborhoodStats.d.ts +10 -0
  39. package/dist/esm/utilities/segmentation/growCut/neighborhoodStats.js +44 -0
  40. package/dist/esm/utilities/segmentation/growCut/runFloodFillSegmentation.d.ts +61 -0
  41. package/dist/esm/utilities/segmentation/growCut/runFloodFillSegmentation.js +524 -0
  42. package/dist/esm/utilities/segmentation/islandRemoval.d.ts +3 -2
  43. package/dist/esm/utilities/segmentation/islandRemoval.js +3 -0
  44. package/dist/esm/version.d.ts +1 -1
  45. package/dist/esm/version.js +1 -1
  46. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import { triggerSegmentationRenderBySegmentationId } from '../../stateManagement/segmentation/SegmentationRenderingEngine.js';
1
+ import { triggerSegmentationRenderForModified } from '../../stateManagement/segmentation/SegmentationRenderingEngine.js';
2
2
  import onLabelmapSegmentationDataModified from './labelmap/onLabelmapSegmentationDataModified.js';
3
3
  import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation.js';
4
4
  const onSegmentationDataModified = function (evt) {
@@ -7,6 +7,6 @@ const onSegmentationDataModified = function (evt) {
7
7
  if (representationData.Labelmap) {
8
8
  onLabelmapSegmentationDataModified(evt);
9
9
  }
10
- triggerSegmentationRenderBySegmentationId(segmentationId);
10
+ triggerSegmentationRenderForModified(segmentationId);
11
11
  };
12
12
  export default onSegmentationDataModified;
@@ -12,7 +12,7 @@ import * as Types from './types/index.js';
12
12
  import * as annotation from './stateManagement/annotation/index.js';
13
13
  import * as segmentation from './stateManagement/segmentation/index.js';
14
14
  import * as splines from './tools/annotation/splines/index.js';
15
- import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool } from './tools/index.js';
15
+ import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool } from './tools/index.js';
16
16
  import VideoRedactionTool from './tools/annotation/VideoRedactionTool.js';
17
17
  import * as Enums from './enums/index.js';
18
- export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
18
+ export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
package/dist/esm/index.js CHANGED
@@ -12,7 +12,7 @@ import * as Types from './types/index.js';
12
12
  import * as annotation from './stateManagement/annotation/index.js';
13
13
  import * as segmentation from './stateManagement/segmentation/index.js';
14
14
  import * as splines from './tools/annotation/splines/index.js';
15
- import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool, } from './tools/index.js';
15
+ import { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, ZoomTool, StackScrollTool, SegmentBidirectionalTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, KeyImageTool, CrosshairsTool, ReferenceLinesTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, OverlayGridTool, SegmentationIntersectionTool, EraserTool, SculptorTool, SegmentSelectTool, WindowLevelRegionTool, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentLabelTool, LabelMapEditWithContourTool, } from './tools/index.js';
16
16
  import VideoRedactionTool from './tools/annotation/VideoRedactionTool.js';
17
17
  import * as Enums from './enums/index.js';
18
- export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
18
+ export { VideoRedactionTool, init, destroy, addTool, removeTool, cancelActiveManipulations, BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, SegmentBidirectionalTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, ZoomTool, StackScrollTool, PlanarRotateTool, MIPJumpToClickTool, LabelTool, LengthTool, HeightTool, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, SplineContourSegmentationTool, BidirectionalTool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, MagnifyTool, AdvancedMagnifyTool, ReferenceCursors, ScaleOverlayTool, SculptorTool, EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, BrushTool, OrientationMarkerTool, OrientationControllerTool, SegmentSelectTool, SegmentLabelTool, synchronizers, Synchronizer, SynchronizerManager, PaintFillTool, Types, state, ToolGroupManager, store, Enums, CONSTANTS, drawing, annotation, segmentation, utilities, cursors, VolumeRotateTool, RegionSegmentPlusTool, RegionSegmentTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, LabelMapEditWithContourTool, splines, version, };
@@ -17,5 +17,6 @@ declare class SegmentationRenderingEngine {
17
17
  }
18
18
  declare function triggerSegmentationRender(viewportId?: string): void;
19
19
  declare function triggerSegmentationRenderBySegmentationId(segmentationId?: string): void;
20
+ declare function triggerSegmentationRenderForModified(segmentationId?: string): void;
20
21
  declare const segmentationRenderingEngine: SegmentationRenderingEngine;
21
- export { triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, segmentationRenderingEngine, };
22
+ export { triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, triggerSegmentationRenderForModified, segmentationRenderingEngine, };
@@ -169,5 +169,49 @@ function triggerSegmentationRender(viewportId) {
169
169
  function triggerSegmentationRenderBySegmentationId(segmentationId) {
170
170
  segmentationRenderingEngine.renderSegmentation(segmentationId);
171
171
  }
172
+ const DEFERRED_SEGMENTATION_RENDER_DELAY_MS = 240;
173
+ const deferredSegmentationRenderTimers = new Map();
174
+ function isProjectionHeavySegmentationViewport(viewport) {
175
+ const blendMode = viewport.getBlendMode?.();
176
+ if (blendMode === Enums.BlendModes.LABELMAP_EDGE_PROJECTION_BLEND) {
177
+ return true;
178
+ }
179
+ const planarViewport = viewport;
180
+ if (typeof planarViewport.getDisplaySetPresentation !== 'function') {
181
+ return false;
182
+ }
183
+ const dataIds = [];
184
+ const sourceDataId = planarViewport.getSourceDataId?.();
185
+ if (sourceDataId) {
186
+ dataIds.push(sourceDataId);
187
+ }
188
+ planarViewport.getActors?.().forEach((actorEntry) => {
189
+ if (actorEntry.representationUID) {
190
+ dataIds.push(String(actorEntry.representationUID));
191
+ }
192
+ });
193
+ return dataIds.some((dataId) => (planarViewport.getDisplaySetPresentation(dataId)?.slabThickness ?? 0) > 0);
194
+ }
195
+ function triggerSegmentationRenderForModified(segmentationId) {
196
+ const viewportIds = segmentationRenderingEngine._getViewportIdsForSegmentation(segmentationId);
197
+ viewportIds.forEach((viewportId) => {
198
+ const { viewport } = getEnabledElementByViewportId(viewportId) || {};
199
+ if (!viewport) {
200
+ return;
201
+ }
202
+ if (!isProjectionHeavySegmentationViewport(viewport)) {
203
+ segmentationRenderingEngine.renderSegmentationsForViewport(viewportId);
204
+ return;
205
+ }
206
+ const existingTimer = deferredSegmentationRenderTimers.get(viewportId);
207
+ if (existingTimer !== undefined) {
208
+ clearTimeout(existingTimer);
209
+ }
210
+ deferredSegmentationRenderTimers.set(viewportId, setTimeout(() => {
211
+ deferredSegmentationRenderTimers.delete(viewportId);
212
+ segmentationRenderingEngine.renderSegmentationsForViewport(viewportId);
213
+ }, DEFERRED_SEGMENTATION_RENDER_DELAY_MS));
214
+ });
215
+ }
172
216
  const segmentationRenderingEngine = new SegmentationRenderingEngine();
173
- export { triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, segmentationRenderingEngine, };
217
+ export { triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, triggerSegmentationRenderForModified, segmentationRenderingEngine, };
@@ -2,7 +2,15 @@ import type { Types } from '@cornerstonejs/core';
2
2
  import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3
3
  import type { Segmentation } from '../../../types/SegmentationStateTypes.js';
4
4
  import type { LabelmapLayer, SegmentLabelmapBindingState } from '../../../types/LabelmapTypes.js';
5
- type MoveSegmentToPrivateLabelmap = (segmentation: Segmentation, segmentIndex: number) => LabelmapLayer | undefined;
5
+ import type { LabelmapRestoreStep } from '../../../utilities/segmentation/createLabelmapMemo.js';
6
+ type CrossLayerEraseRecord = {
7
+ voxelManager: Types.IVoxelManager<number>;
8
+ labelValue: number;
9
+ indices: number[];
10
+ };
11
+ type MoveSegmentToPrivateLabelmap = (segmentation: Segmentation, segmentIndex: number, options?: {
12
+ moveStepCallback?: (step: LabelmapRestoreStep) => void;
13
+ }) => LabelmapLayer | undefined;
6
14
  type BeginLabelmapEditTransactionOptions = {
7
15
  segmentIndex: number;
8
16
  overwriteSegmentIndices?: number[];
@@ -22,6 +30,7 @@ type LabelmapEditTransaction = {
22
30
  protectedSegmentIndices: number[];
23
31
  crossLayerEraseBindings: SegmentLabelmapBindingState[];
24
32
  movedSegment: boolean;
33
+ moveStep?: LabelmapRestoreStep;
25
34
  };
26
35
  type ResolveLabelmapLayerEditTargetOptions = {
27
36
  viewport?: Types.IViewport;
@@ -42,6 +51,7 @@ type EraseLabelmapEditTransactionOptions = {
42
51
  isInObject: (point: Types.Point3) => boolean;
43
52
  isInObjectBoundsIJK?: Types.BoundsIJK;
44
53
  imageId?: string;
54
+ crossLayerEraseCallback?: (record: CrossLayerEraseRecord) => void;
45
55
  };
46
56
  declare function getProtectedSegmentIndicesForLayer(segmentation: Segmentation, labelmapId: string, segmentIndex: number, overwriteSegmentIndices?: number[]): number[];
47
57
  declare function hasProtectedSegmentOverwrite(segmentation: Segmentation, labelmapId: string, protectedSegmentIndices: number[], voxelManager?: Types.IVoxelManager<number>, options?: Pick<BeginLabelmapEditTransactionOptions, 'segmentationImageData' | 'isInObject' | 'isInObjectBoundsIJK'>): boolean;
@@ -51,4 +61,4 @@ declare function getLabelmapLayerImageId(layer: LabelmapLayer, options?: Resolve
51
61
  declare function resolveLabelmapLayerEditTarget(layer: LabelmapLayer, options?: ResolveLabelmapLayerEditTargetOptions): LabelmapLayerEditTarget;
52
62
  declare function eraseLabelmapEditTransactionOverwrites(segmentation: Segmentation, transaction: LabelmapEditTransaction | undefined, options: EraseLabelmapEditTransactionOptions): number[];
53
63
  export { beginLabelmapEditTransaction, collectCrossLayerEraseBindings, eraseLabelmapEditTransactionOverwrites, getLabelmapLayerImageId, getProtectedSegmentIndicesForLayer, hasProtectedSegmentOverwrite, resolveLabelmapLayerEditTarget, };
54
- export type { BeginLabelmapEditTransactionOptions, EraseLabelmapEditTransactionOptions, LabelmapEditTransaction, LabelmapLayerEditTarget, ResolveLabelmapLayerEditTargetOptions, };
64
+ export type { BeginLabelmapEditTransactionOptions, CrossLayerEraseRecord, EraseLabelmapEditTransactionOptions, LabelmapEditTransaction, LabelmapLayerEditTarget, ResolveLabelmapLayerEditTargetOptions, };
@@ -72,10 +72,11 @@ function beginLabelmapEditTransaction(segmentation, options) {
72
72
  isInObject: options.isInObject,
73
73
  isInObjectBoundsIJK: options.isInObjectBoundsIJK,
74
74
  });
75
+ let moveStep;
75
76
  if (shouldMoveSegment) {
76
77
  const moveSegmentToPrivateLabelmap = options.moveSegmentToPrivateLabelmap ??
77
78
  defaultMoveSegmentToPrivateLabelmap;
78
- const privateLayer = moveSegmentToPrivateLabelmap(segmentation, segmentIndex);
79
+ const privateLayer = moveSegmentToPrivateLabelmap(segmentation, segmentIndex, { moveStepCallback: (step) => (moveStep = step) });
79
80
  const privateBinding = getSegmentBinding(segmentation, segmentIndex);
80
81
  if (privateLayer && privateBinding) {
81
82
  activeLayer = privateLayer;
@@ -100,6 +101,7 @@ function beginLabelmapEditTransaction(segmentation, options) {
100
101
  protectedSegmentIndices,
101
102
  crossLayerEraseBindings: collectCrossLayerEraseBindings(segmentation, labelmapId, overwriteSegmentIndices),
102
103
  movedSegment,
104
+ moveStep,
103
105
  };
104
106
  }
105
107
  function getViewportImageIds(viewport) {
@@ -158,6 +160,7 @@ function eraseVolumeLayer(layer, binding, options, modifiedSlices) {
158
160
  if (!volume) {
159
161
  return;
160
162
  }
163
+ const erasedIndices = [];
161
164
  volume.voxelManager.forEach(({ value, index, pointIJK }) => {
162
165
  if (value !== binding.labelValue) {
163
166
  return;
@@ -167,10 +170,18 @@ function eraseVolumeLayer(layer, binding, options, modifiedSlices) {
167
170
  return;
168
171
  }
169
172
  volume.voxelManager.setAtIndex(index, 0);
173
+ erasedIndices.push(index);
170
174
  }, {
171
175
  imageData: volume.imageData,
172
176
  boundsIJK: options.isInObjectBoundsIJK,
173
177
  });
178
+ if (erasedIndices.length) {
179
+ options.crossLayerEraseCallback?.({
180
+ voxelManager: volume.voxelManager,
181
+ labelValue: binding.labelValue,
182
+ indices: erasedIndices,
183
+ });
184
+ }
174
185
  volume.voxelManager
175
186
  ?.getArrayOfModifiedSlices?.()
176
187
  ?.forEach((sliceIndex) => modifiedSlices.add(sliceIndex));
@@ -185,6 +196,7 @@ function eraseStackLayer(layer, binding, options, modifiedSlices) {
185
196
  if (!image || !voxelManager) {
186
197
  return;
187
198
  }
199
+ const erasedIndices = [];
188
200
  voxelManager.forEach(({ value, index, pointIJK }) => {
189
201
  if (value !== binding.labelValue) {
190
202
  return;
@@ -194,10 +206,18 @@ function eraseStackLayer(layer, binding, options, modifiedSlices) {
194
206
  return;
195
207
  }
196
208
  voxelManager.setAtIndex(index, 0);
209
+ erasedIndices.push(index);
197
210
  }, {
198
211
  imageData: options.referenceImageData,
199
212
  boundsIJK: options.isInObjectBoundsIJK,
200
213
  });
214
+ if (erasedIndices.length) {
215
+ options.crossLayerEraseCallback?.({
216
+ voxelManager,
217
+ labelValue: binding.labelValue,
218
+ indices: erasedIndices,
219
+ });
220
+ }
201
221
  const currentSlice = stackViewport.getCurrentImageIdIndex?.();
202
222
  if (typeof currentSlice === 'number') {
203
223
  modifiedSlices.add(currentSlice);
@@ -5,6 +5,7 @@ declare class LabelmapImageReferenceResolver {
5
5
  private readonly stackLabelmapImageIdReferenceMap;
6
6
  private readonly labelmapImageIdReferenceMap;
7
7
  private readonly keysBySegmentationId;
8
+ private readonly completedScanResultsBySegmentationId;
8
9
  constructor(getSegmentation: GetSegmentation);
9
10
  reset(): void;
10
11
  removeSegmentation(segmentationId: string): void;
@@ -12,6 +13,10 @@ declare class LabelmapImageReferenceResolver {
12
13
  getLabelmapImageIds(representationData: RepresentationsData): any;
13
14
  getLabelmapImageIdsForImageId(imageId: string, segmentationId: string): string[];
14
15
  updateLabelmapSegmentationImageReferences(viewportId: string, segmentationId: string): string | undefined;
16
+ private generateScanKey;
17
+ private hasCompletedScan;
18
+ private getCompletedScanResult;
19
+ private markCompletedScan;
15
20
  getCurrentLabelmapImageIdsForViewport(viewportId: string, segmentationId: string): string[] | undefined;
16
21
  getCurrentLabelmapImageIdForViewport(viewportId: string, segmentationId: string): string | undefined;
17
22
  getStackSegmentationImageIdsForViewport(viewportId: string, segmentationId: string): string[];
@@ -10,15 +10,18 @@ class LabelmapImageReferenceResolver {
10
10
  this.stackLabelmapImageIdReferenceMap = new Map();
11
11
  this.labelmapImageIdReferenceMap = new Map();
12
12
  this.keysBySegmentationId = new Map();
13
+ this.completedScanResultsBySegmentationId = new Map();
13
14
  this.getSegmentation = getSegmentation;
14
15
  }
15
16
  reset() {
16
17
  this.stackLabelmapImageIdReferenceMap.clear();
17
18
  this.labelmapImageIdReferenceMap.clear();
18
19
  this.keysBySegmentationId.clear();
20
+ this.completedScanResultsBySegmentationId.clear();
19
21
  }
20
22
  removeSegmentation(segmentationId) {
21
23
  this.stackLabelmapImageIdReferenceMap.delete(segmentationId);
24
+ this.completedScanResultsBySegmentationId.delete(segmentationId);
22
25
  const keys = this.keysBySegmentationId.get(segmentationId);
23
26
  if (keys) {
24
27
  for (const key of keys) {
@@ -92,7 +95,43 @@ class LabelmapImageReferenceResolver {
92
95
  if (!enabledElement || !labelmapImageIds?.length) {
93
96
  return;
94
97
  }
95
- return this.updateLabelmapSegmentationReferences(segmentationId, enabledElement.viewport, labelmapImageIds);
98
+ const viewport = enabledElement.viewport;
99
+ const scanKey = this.generateScanKey('current', viewport, labelmapImageIds);
100
+ if (scanKey && this.hasCompletedScan(segmentationId, scanKey)) {
101
+ return this.getCompletedScanResult(segmentationId, scanKey);
102
+ }
103
+ const result = this.updateLabelmapSegmentationReferences(segmentationId, viewport, labelmapImageIds);
104
+ if (scanKey) {
105
+ this.markCompletedScan(segmentationId, scanKey, result);
106
+ }
107
+ return result;
108
+ }
109
+ generateScanKey(kind, viewport, labelmapImageIds) {
110
+ const referenceImageId = typeof viewport.getCurrentImageId === 'function'
111
+ ? viewport.getCurrentImageId()
112
+ : undefined;
113
+ if (!referenceImageId) {
114
+ return;
115
+ }
116
+ return `${kind}|${viewport.id}|${referenceImageId}|${labelmapImageIds.length}|${labelmapImageIds[0]}|${labelmapImageIds[labelmapImageIds.length - 1]}`;
117
+ }
118
+ hasCompletedScan(segmentationId, scanKey) {
119
+ return !!this.completedScanResultsBySegmentationId
120
+ .get(segmentationId)
121
+ ?.has(scanKey);
122
+ }
123
+ getCompletedScanResult(segmentationId, scanKey) {
124
+ return this.completedScanResultsBySegmentationId
125
+ .get(segmentationId)
126
+ ?.get(scanKey);
127
+ }
128
+ markCompletedScan(segmentationId, scanKey, result) {
129
+ let results = this.completedScanResultsBySegmentationId.get(segmentationId);
130
+ if (!results) {
131
+ results = new Map();
132
+ this.completedScanResultsBySegmentationId.set(segmentationId, results);
133
+ }
134
+ results.set(scanKey, result);
96
135
  }
97
136
  getCurrentLabelmapImageIdsForViewport(viewportId, segmentationId) {
98
137
  const enabledElement = getEnabledElementByViewportId(viewportId);
@@ -233,6 +272,13 @@ class LabelmapImageReferenceResolver {
233
272
  return;
234
273
  }
235
274
  const stackViewport = enabledElement.viewport;
275
+ const scanKey = this.generateScanKey('all', stackViewport, labelmapImageIds);
276
+ if (scanKey && this.hasCompletedScan(segmentationId, scanKey)) {
277
+ return;
278
+ }
279
+ if (scanKey) {
280
+ this.markCompletedScan(segmentationId, scanKey);
281
+ }
236
282
  this.updateLabelmapSegmentationReferences(segmentationId, stackViewport, labelmapImageIds, (stackViewport, segmentationId, labelmapImageIds) => {
237
283
  const imageIds = stackViewport.getImageIds();
238
284
  imageIds.forEach((referenceImageId, index) => {
@@ -1,5 +1,9 @@
1
1
  import type { Segmentation } from '../../../types/SegmentationStateTypes.js';
2
2
  import type { LabelmapLayer } from '../../../types/LabelmapTypes.js';
3
+ import type { LabelmapRestoreStep } from '../../../utilities/segmentation/createLabelmapMemo.js';
4
+ type MoveSegmentToPrivateLabelmapOptions = {
5
+ moveStepCallback?: (step: LabelmapRestoreStep) => void;
6
+ };
3
7
  declare function createPrivateLabelmap(segmentation: Segmentation, sourceLabelmap: LabelmapLayer): LabelmapLayer;
4
- declare function moveSegmentToPrivateLabelmap(segmentation: Segmentation, segmentIndex: number): LabelmapLayer | undefined;
8
+ declare function moveSegmentToPrivateLabelmap(segmentation: Segmentation, segmentIndex: number, options?: MoveSegmentToPrivateLabelmapOptions): LabelmapLayer | undefined;
5
9
  export { createPrivateLabelmap, moveSegmentToPrivateLabelmap };
@@ -1,5 +1,5 @@
1
1
  import { cache, imageLoader, utilities as csUtils, volumeLoader, } from '@cornerstonejs/core';
2
- import { getLabelmap, registerLabelmap } from './labelmapLayerStore.js';
2
+ import { getLabelmap, registerLabelmap, removeLabelmap, } from './labelmapLayerStore.js';
3
3
  import { getSegmentBinding, getSegmentsOnLabelmap, setSegmentBinding, } from './labelmapSegmentBindings.js';
4
4
  import { syncLegacyLabelmapData } from './labelmapLegacyAdapter.js';
5
5
  function createPrivateVolumeLabelmap(segmentation, sourceLabelmap) {
@@ -53,7 +53,7 @@ function createPrivateLabelmap(segmentation, sourceLabelmap) {
53
53
  }
54
54
  return createPrivateStackLabelmap(segmentation, sourceLabelmap);
55
55
  }
56
- function moveSegmentToPrivateLabelmap(segmentation, segmentIndex) {
56
+ function moveSegmentToPrivateLabelmap(segmentation, segmentIndex, options = {}) {
57
57
  const binding = getSegmentBinding(segmentation, segmentIndex);
58
58
  if (!binding) {
59
59
  return;
@@ -67,16 +67,26 @@ function moveSegmentToPrivateLabelmap(segmentation, segmentIndex) {
67
67
  }
68
68
  const privateLabelmap = createPrivateLabelmap(segmentation, sourceLabelmap);
69
69
  registerLabelmap(segmentation, privateLabelmap);
70
+ const movedVoxels = [];
70
71
  if (sourceLabelmap.volumeId && privateLabelmap.volumeId) {
71
72
  const sourceVolume = cache.getVolume(sourceLabelmap.volumeId);
72
73
  const targetVolume = cache.getVolume(privateLabelmap.volumeId);
74
+ const indices = [];
73
75
  sourceVolume.voxelManager.forEach(({ value, index }) => {
74
76
  if (value !== binding.labelValue) {
75
77
  return;
76
78
  }
77
79
  targetVolume.voxelManager.setAtIndex(index, 1);
78
80
  sourceVolume.voxelManager.setAtIndex(index, 0);
81
+ indices.push(index);
79
82
  });
83
+ if (indices.length) {
84
+ movedVoxels.push({
85
+ source: sourceVolume.voxelManager,
86
+ target: targetVolume.voxelManager,
87
+ indices,
88
+ });
89
+ }
80
90
  }
81
91
  else {
82
92
  const sourceImageIds = sourceLabelmap.imageIds ?? [];
@@ -87,20 +97,55 @@ function moveSegmentToPrivateLabelmap(segmentation, segmentIndex) {
87
97
  if (!sourceImage || !targetImage) {
88
98
  return;
89
99
  }
100
+ const indices = [];
90
101
  sourceImage.voxelManager.forEach(({ value, index }) => {
91
102
  if (value !== binding.labelValue) {
92
103
  return;
93
104
  }
94
105
  targetImage.voxelManager.setAtIndex(index, 1);
95
106
  sourceImage.voxelManager.setAtIndex(index, 0);
107
+ indices.push(index);
96
108
  });
109
+ if (indices.length) {
110
+ movedVoxels.push({
111
+ source: sourceImage.voxelManager,
112
+ target: targetImage.voxelManager,
113
+ indices,
114
+ });
115
+ }
97
116
  });
98
117
  }
99
- setSegmentBinding(segmentation, segmentIndex, {
118
+ const previousBinding = { ...binding };
119
+ const newBinding = {
100
120
  labelmapId: privateLabelmap.labelmapId,
101
121
  labelValue: 1,
102
- });
122
+ };
123
+ setSegmentBinding(segmentation, segmentIndex, { ...newBinding });
103
124
  syncLegacyLabelmapData(segmentation);
125
+ options.moveStepCallback?.({
126
+ undo: () => {
127
+ for (const { source, target, indices } of movedVoxels) {
128
+ for (const index of indices) {
129
+ target.setAtIndex(index, 0);
130
+ source.setAtIndex(index, previousBinding.labelValue);
131
+ }
132
+ }
133
+ setSegmentBinding(segmentation, segmentIndex, { ...previousBinding });
134
+ removeLabelmap(segmentation, privateLabelmap.labelmapId);
135
+ syncLegacyLabelmapData(segmentation);
136
+ },
137
+ redo: () => {
138
+ registerLabelmap(segmentation, privateLabelmap);
139
+ for (const { source, target, indices } of movedVoxels) {
140
+ for (const index of indices) {
141
+ source.setAtIndex(index, 0);
142
+ target.setAtIndex(index, 1);
143
+ }
144
+ }
145
+ setSegmentBinding(segmentation, segmentIndex, { ...newBinding });
146
+ syncLegacyLabelmapData(segmentation);
147
+ },
148
+ });
104
149
  return privateLabelmap;
105
150
  }
106
151
  export { createPrivateLabelmap, moveSegmentToPrivateLabelmap };
@@ -0,0 +1,51 @@
1
+ import type { EventTypes, PublicToolProps, ToolProps } from '../../types/index.js';
2
+ import GrowCutBaseTool from '../base/GrowCutBaseTool.js';
3
+ declare class ClickSegmentTool extends GrowCutBaseTool {
4
+ static toolName: string;
5
+ private segmentationInProgress;
6
+ private hoverThrottleTimer;
7
+ private pendingHoverEvent;
8
+ private lastHoverElement;
9
+ private lastProbe;
10
+ private lastClick;
11
+ private clickAbortController;
12
+ private dryRunToken;
13
+ private confirmedRegion;
14
+ private rejectedRegions;
15
+ constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
16
+ private notifySegmentationError;
17
+ cancelInProgress(): boolean;
18
+ cancelActiveOperation(): boolean;
19
+ private clearHoverState;
20
+ private pointerOnConfirmedRegion;
21
+ private static bandsAreSimilar;
22
+ private matchesRejectedRegion;
23
+ private rememberRejectedRegion;
24
+ private safeSpacing;
25
+ private computeVoxelBudget;
26
+ private makeRegionShapeGate;
27
+ onSetToolPassive(): void;
28
+ onSetToolDisabled(): void;
29
+ private buildProbeOptions;
30
+ mouseMoveCallback(evt: EventTypes.MouseMoveEventType): void;
31
+ private queueHoverProbe;
32
+ private flushHoverProbe;
33
+ private getViewportSliceIndex;
34
+ private runHoverProbe;
35
+ private neighborsAgree;
36
+ private runConfinementDryRun;
37
+ private probeAppliesToClick;
38
+ preMouseDownCallback(evt: EventTypes.MouseDownActivateEventType): Promise<boolean>;
39
+ private runClick;
40
+ private fillWithRange;
41
+ private indexToWorld;
42
+ private beginLabelmapMemo;
43
+ private curveSizeAt;
44
+ private pickNextTolerance;
45
+ expand(): void;
46
+ shrink(): void;
47
+ refresh(): void;
48
+ private stepTolerance;
49
+ private refillAtTolerance;
50
+ }
51
+ export default ClickSegmentTool;