@cornerstonejs/tools 3.7.17 → 3.8.1

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 (32) hide show
  1. package/dist/esm/enums/Events.d.ts +2 -0
  2. package/dist/esm/enums/Events.js +2 -0
  3. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.d.ts +1 -0
  4. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.js +8 -5
  5. package/dist/esm/stateManagement/segmentation/config/segmentationColor.js +2 -1
  6. package/dist/esm/stateManagement/segmentation/getCurrentLabelmapImageIdForViewport.d.ts +1 -0
  7. package/dist/esm/stateManagement/segmentation/getCurrentLabelmapImageIdForViewport.js +4 -0
  8. package/dist/esm/stateManagement/segmentation/index.d.ts +3 -1
  9. package/dist/esm/stateManagement/segmentation/index.js +3 -1
  10. package/dist/esm/tools/base/AnnotationTool.d.ts +2 -0
  11. package/dist/esm/tools/base/AnnotationTool.js +2 -0
  12. package/dist/esm/tools/segmentation/BrushTool.d.ts +1 -1
  13. package/dist/esm/tools/segmentation/BrushTool.js +45 -17
  14. package/dist/esm/tools/segmentation/LabelmapBaseTool.d.ts +15 -2
  15. package/dist/esm/tools/segmentation/LabelmapBaseTool.js +69 -16
  16. package/dist/esm/tools/segmentation/strategies/BrushStrategy.d.ts +8 -4
  17. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +8 -30
  18. package/dist/esm/tools/segmentation/strategies/compositions/determineSegmentIndex.d.ts +0 -1
  19. package/dist/esm/tools/segmentation/strategies/compositions/determineSegmentIndex.js +6 -25
  20. package/dist/esm/tools/segmentation/strategies/compositions/index.d.ts +0 -1
  21. package/dist/esm/tools/segmentation/strategies/compositions/islandRemovalComposition.js +2 -2
  22. package/dist/esm/tools/segmentation/strategies/compositions/preview.js +38 -46
  23. package/dist/esm/tools/segmentation/strategies/compositions/regionFill.js +2 -2
  24. package/dist/esm/tools/segmentation/strategies/compositions/setValue.js +23 -17
  25. package/dist/esm/tools/segmentation/strategies/utils/getStrategyData.js +3 -0
  26. package/dist/esm/tools/segmentation/strategies/utils/handleUseSegmentCenterIndex.d.ts +5 -0
  27. package/dist/esm/tools/segmentation/strategies/utils/handleUseSegmentCenterIndex.js +84 -0
  28. package/dist/esm/types/LabelmapToolOperationData.d.ts +1 -1
  29. package/dist/esm/utilities/segmentation/createLabelmapMemo.d.ts +6 -19
  30. package/dist/esm/utilities/segmentation/createLabelmapMemo.js +6 -18
  31. package/dist/esm/utilities/segmentation/islandRemoval.js +2 -2
  32. package/package.json +3 -3
@@ -22,6 +22,8 @@ declare enum Events {
22
22
  SEGMENTATION_REMOVED = "CORNERSTONE_TOOLS_SEGMENTATION_REMOVED",
23
23
  SEGMENTATION_REPRESENTATION_REMOVED = "CORNERSTONE_TOOLS_SEGMENTATION_REPRESENTATION_REMOVED",
24
24
  SEGMENTATION_DATA_MODIFIED = "CORNERSTONE_TOOLS_SEGMENTATION_DATA_MODIFIED",
25
+ HISTORY_UNDO = "CORNERSTONE_TOOLS_HISTORY_UNDO",
26
+ HISTORY_REDO = "CORNERSTONE_TOOLS_HISTORY_REDO",
25
27
  KEY_DOWN = "CORNERSTONE_TOOLS_KEY_DOWN",
26
28
  KEY_UP = "CORNERSTONE_TOOLS_KEY_UP",
27
29
  MOUSE_DOWN = "CORNERSTONE_TOOLS_MOUSE_DOWN",
@@ -23,6 +23,8 @@ var Events;
23
23
  Events["SEGMENTATION_REMOVED"] = "CORNERSTONE_TOOLS_SEGMENTATION_REMOVED";
24
24
  Events["SEGMENTATION_REPRESENTATION_REMOVED"] = "CORNERSTONE_TOOLS_SEGMENTATION_REPRESENTATION_REMOVED";
25
25
  Events["SEGMENTATION_DATA_MODIFIED"] = "CORNERSTONE_TOOLS_SEGMENTATION_DATA_MODIFIED";
26
+ Events["HISTORY_UNDO"] = "CORNERSTONE_TOOLS_HISTORY_UNDO";
27
+ Events["HISTORY_REDO"] = "CORNERSTONE_TOOLS_HISTORY_REDO";
26
28
  Events["KEY_DOWN"] = "CORNERSTONE_TOOLS_KEY_DOWN";
27
29
  Events["KEY_UP"] = "CORNERSTONE_TOOLS_KEY_UP";
28
30
  Events["MOUSE_DOWN"] = "CORNERSTONE_TOOLS_MOUSE_DOWN";
@@ -24,6 +24,7 @@ export default class SegmentationStateManager {
24
24
  updateLabelmapSegmentationImageReferences(viewportId: any, segmentationId: any): string;
25
25
  _updateAllLabelmapSegmentationImageReferences(viewportId: any, segmentationId: any): void;
26
26
  getLabelmapImageIds(representationData: RepresentationsData): any;
27
+ getLabelmapImageIdsForImageId(imageId: string, segmentationId: string): string[];
27
28
  getCurrentLabelmapImageIdsForViewport(viewportId: string, segmentationId: string): string[] | undefined;
28
29
  getCurrentLabelmapImageIdForViewport(viewportId: string, segmentationId: string): string | undefined;
29
30
  getStackSegmentationImageIdsForViewport(viewportId: string, segmentationId: string): string[];
@@ -253,6 +253,13 @@ export default class SegmentationStateManager {
253
253
  }
254
254
  return labelmapImageIds;
255
255
  }
256
+ getLabelmapImageIdsForImageId(imageId, segmentationId) {
257
+ const key = this._generateMapKey({
258
+ segmentationId,
259
+ referenceImageId: imageId,
260
+ });
261
+ return this._labelmapImageIdReferenceMap.get(key);
262
+ }
256
263
  getCurrentLabelmapImageIdsForViewport(viewportId, segmentationId) {
257
264
  const enabledElement = getEnabledElementByViewportId(viewportId);
258
265
  if (!enabledElement) {
@@ -260,11 +267,7 @@ export default class SegmentationStateManager {
260
267
  }
261
268
  const stackViewport = enabledElement.viewport;
262
269
  const referenceImageId = stackViewport.getCurrentImageId();
263
- const key = this._generateMapKey({
264
- segmentationId,
265
- referenceImageId,
266
- });
267
- return this._labelmapImageIdReferenceMap.get(key);
270
+ return this.getLabelmapImageIdsForImageId(referenceImageId, segmentationId);
268
271
  }
269
272
  getCurrentLabelmapImageIdForViewport(viewportId, segmentationId) {
270
273
  const enabledElement = getEnabledElementByViewportId(viewportId);
@@ -34,7 +34,8 @@ function getSegmentIndexColor(viewportId, segmentationId, segmentIndex) {
34
34
  let colorValue = colorLUT[segmentIndex];
35
35
  if (!colorValue) {
36
36
  if (typeof segmentIndex !== 'number') {
37
- throw new Error(`Can't create colour for LUT index ${segmentIndex}`);
37
+ console.warn(`Can't create colour for LUT index ${segmentIndex}`);
38
+ return null;
38
39
  }
39
40
  colorValue = colorLUT[segmentIndex] = [0, 0, 0, 0];
40
41
  }
@@ -1,2 +1,3 @@
1
1
  export declare function getCurrentLabelmapImageIdForViewport(viewportId: string, segmentationId: string): string;
2
2
  export declare function getCurrentLabelmapImageIdsForViewport(viewportId: string, segmentationId: string): string[];
3
+ export declare function getLabelmapImageIdsForImageId(imageId: string, segmentationId: string): string[];
@@ -7,3 +7,7 @@ export function getCurrentLabelmapImageIdsForViewport(viewportId, segmentationId
7
7
  const segmentationStateManager = defaultSegmentationStateManager;
8
8
  return segmentationStateManager.getCurrentLabelmapImageIdsForViewport(viewportId, segmentationId);
9
9
  }
10
+ export function getLabelmapImageIdsForImageId(imageId, segmentationId) {
11
+ const segmentationStateManager = defaultSegmentationStateManager;
12
+ return segmentationStateManager.getLabelmapImageIdsForImageId(imageId, segmentationId);
13
+ }
@@ -19,10 +19,12 @@ import * as strategies from './../../tools/segmentation/strategies';
19
19
  import { removeAllSegmentations, removeSegmentation } from './removeSegmentation';
20
20
  import { segmentationStyle } from './SegmentationStyle';
21
21
  import { defaultSegmentationStateManager } from './SegmentationStateManager';
22
+ import { getCurrentLabelmapImageIdsForViewport, getLabelmapImageIdsForImageId } from './getCurrentLabelmapImageIdForViewport';
23
+ import { getActiveSegmentation } from './getActiveSegmentation';
22
24
  declare const helpers: {
23
25
  clearSegmentValue: typeof clearSegmentValue;
24
26
  convertStackToVolumeLabelmap: typeof convertStackToVolumeLabelmap;
25
27
  computeVolumeLabelmapFromStack: typeof computeVolumeLabelmapFromStack;
26
28
  convertVolumeToStackLabelmap: typeof convertVolumeToStackLabelmap;
27
29
  };
28
- export { removeSegmentationRepresentation, removeContourRepresentation, removeLabelmapRepresentation, removeSurfaceRepresentation, removeAllSegmentations, removeSegmentation, removeSegmentationRepresentations, addLabelmapRepresentationToViewport, addLabelmapRepresentationToViewportMap, addSegmentationRepresentations, removeAllSegmentationRepresentations, addContourRepresentationToViewport, addContourRepresentationToViewportMap, addSurfaceRepresentationToViewport, addSurfaceRepresentationToViewportMap, addSegmentations, updateSegmentations, state, activeSegmentation, segmentLocking, config, segmentIndex, triggerSegmentationEvents, helpers, removeSegment, getLabelmapImageIds, addRepresentationData, strategies, segmentationStyle, defaultSegmentationStateManager, };
30
+ export { removeSegmentationRepresentation, removeContourRepresentation, removeLabelmapRepresentation, removeSurfaceRepresentation, removeAllSegmentations, removeSegmentation, removeSegmentationRepresentations, addLabelmapRepresentationToViewport, addLabelmapRepresentationToViewportMap, addSegmentationRepresentations, removeAllSegmentationRepresentations, addContourRepresentationToViewport, addContourRepresentationToViewportMap, addSurfaceRepresentationToViewport, addSurfaceRepresentationToViewportMap, addSegmentations, updateSegmentations, state, activeSegmentation, segmentLocking, config, segmentIndex, triggerSegmentationEvents, helpers, removeSegment, getLabelmapImageIds, addRepresentationData, strategies, segmentationStyle, defaultSegmentationStateManager, getCurrentLabelmapImageIdsForViewport, getLabelmapImageIdsForImageId, getActiveSegmentation, };
@@ -19,10 +19,12 @@ import * as strategies from './../../tools/segmentation/strategies';
19
19
  import { removeAllSegmentations, removeSegmentation, } from './removeSegmentation';
20
20
  import { segmentationStyle } from './SegmentationStyle';
21
21
  import { defaultSegmentationStateManager } from './SegmentationStateManager';
22
+ import { getCurrentLabelmapImageIdsForViewport, getLabelmapImageIdsForImageId, } from './getCurrentLabelmapImageIdForViewport';
23
+ import { getActiveSegmentation } from './getActiveSegmentation';
22
24
  const helpers = {
23
25
  clearSegmentValue,
24
26
  convertStackToVolumeLabelmap,
25
27
  computeVolumeLabelmapFromStack,
26
28
  convertVolumeToStackLabelmap,
27
29
  };
28
- export { removeSegmentationRepresentation, removeContourRepresentation, removeLabelmapRepresentation, removeSurfaceRepresentation, removeAllSegmentations, removeSegmentation, removeSegmentationRepresentations, addLabelmapRepresentationToViewport, addLabelmapRepresentationToViewportMap, addSegmentationRepresentations, removeAllSegmentationRepresentations, addContourRepresentationToViewport, addContourRepresentationToViewportMap, addSurfaceRepresentationToViewport, addSurfaceRepresentationToViewportMap, addSegmentations, updateSegmentations, state, activeSegmentation, segmentLocking, config, segmentIndex, triggerSegmentationEvents, helpers, removeSegment, getLabelmapImageIds, addRepresentationData, strategies, segmentationStyle, defaultSegmentationStateManager, };
30
+ export { removeSegmentationRepresentation, removeContourRepresentation, removeLabelmapRepresentation, removeSurfaceRepresentation, removeAllSegmentations, removeSegmentation, removeSegmentationRepresentations, addLabelmapRepresentationToViewport, addLabelmapRepresentationToViewportMap, addSegmentationRepresentations, removeAllSegmentationRepresentations, addContourRepresentationToViewport, addContourRepresentationToViewportMap, addSurfaceRepresentationToViewport, addSurfaceRepresentationToViewportMap, addSegmentations, updateSegmentations, state, activeSegmentation, segmentLocking, config, segmentIndex, triggerSegmentationEvents, helpers, removeSegment, getLabelmapImageIds, addRepresentationData, strategies, segmentationStyle, defaultSegmentationStateManager, getCurrentLabelmapImageIdsForViewport, getLabelmapImageIdsForImageId, getActiveSegmentation, };
@@ -53,6 +53,8 @@ declare abstract class AnnotationTool extends AnnotationDisplayTool {
53
53
  deleting?: boolean;
54
54
  }): {
55
55
  restoreMemo: () => void;
56
+ id: string;
57
+ operationType: string;
56
58
  };
57
59
  protected createMemo(element: any, annotation: any, options?: any): void;
58
60
  protected static hydrateBase<T extends AnnotationTool>(ToolClass: new () => T, enabledElement: Types.IEnabledElement, points: Types.Point3[], options?: {
@@ -259,6 +259,8 @@ class AnnotationTool extends AnnotationDisplayTool {
259
259
  currentAnnotation.invalidated = true;
260
260
  triggerAnnotationModified(currentAnnotation, element, ChangeTypes.History);
261
261
  },
262
+ id: annotationUID,
263
+ operationType: 'annotation',
262
264
  };
263
265
  DefaultHistoryMemo.push(annotationMemo);
264
266
  return annotationMemo;
@@ -17,8 +17,8 @@ declare class BrushTool extends LabelmapBaseTool {
17
17
  private _endCallback;
18
18
  getStatistics(element: any, segmentIndices?: any): any;
19
19
  rejectPreview(element?: HTMLDivElement): void;
20
- interpolate(element: any, config: any): void;
21
20
  acceptPreview(element?: HTMLDivElement): void;
21
+ interpolate(element: any, config: any): void;
22
22
  private _activateDraw;
23
23
  private _deactivateDraw;
24
24
  invalidateBrushCursor(): void;
@@ -1,14 +1,15 @@
1
- import { getEnabledElement } from '@cornerstonejs/core';
1
+ import { getEnabledElement, eventTarget } from '@cornerstonejs/core';
2
2
  import { vec3, vec2 } from 'gl-matrix';
3
+ import { Events, ToolModes, StrategyCallbacks } from '../../enums';
3
4
  import { fillInsideSphere, thresholdInsideSphere, thresholdInsideSphereIsland, } from './strategies/fillSphere';
4
5
  import { eraseInsideSphere } from './strategies/eraseSphere';
5
6
  import { thresholdInsideCircle, fillInsideCircle, } from './strategies/fillCircle';
6
7
  import { eraseInsideCircle } from './strategies/eraseCircle';
7
- import { Events, ToolModes, StrategyCallbacks } from '../../enums';
8
8
  import { drawCircle as drawCircleSvg } from '../../drawingSvg';
9
9
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
10
10
  import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
11
11
  import LabelmapBaseTool from './LabelmapBaseTool';
12
+ import { getStrategyData } from './strategies/utils/getStrategyData';
12
13
  class BrushTool extends LabelmapBaseTool {
13
14
  constructor(toolProps = {}, defaultToolProps = {
14
15
  supportedInteractionTypes: ['Mouse', 'Touch'],
@@ -25,6 +26,7 @@ class BrushTool extends LabelmapBaseTool {
25
26
  defaultStrategy: 'FILL_INSIDE_CIRCLE',
26
27
  activeStrategy: 'FILL_INSIDE_CIRCLE',
27
28
  brushSize: 25,
29
+ useCenterSegmentIndex: false,
28
30
  preview: {
29
31
  enabled: false,
30
32
  previewColors: {
@@ -112,7 +114,10 @@ class BrushTool extends LabelmapBaseTool {
112
114
  const { previewTimeMs, previewMoveDistance, dragMoveDistance } = this.configuration.preview;
113
115
  const { currentPoints, element } = evt.detail;
114
116
  const { canvas } = currentPoints;
115
- const { preview, startPoint, timer, timerStart, isDrag } = this._previewData;
117
+ const { startPoint, timer, timerStart, isDrag } = this._previewData;
118
+ if (isDrag) {
119
+ return;
120
+ }
116
121
  const delta = vec2.distance(canvas, startPoint);
117
122
  const time = Date.now() - timerStart;
118
123
  if (delta > previewMoveDistance ||
@@ -121,7 +126,7 @@ class BrushTool extends LabelmapBaseTool {
121
126
  window.clearTimeout(timer);
122
127
  this._previewData.timer = null;
123
128
  }
124
- if (preview && !isDrag) {
129
+ if (!isDrag) {
125
130
  this.rejectPreview(element);
126
131
  }
127
132
  }
@@ -137,11 +142,32 @@ class BrushTool extends LabelmapBaseTool {
137
142
  }
138
143
  };
139
144
  this.previewCallback = () => {
145
+ if (this._previewData.isDrag) {
146
+ this._previewData.timer = null;
147
+ return;
148
+ }
140
149
  this._previewData.timer = null;
141
- if (this._previewData.preview) {
150
+ const operationData = this.getOperationData(this._previewData.element);
151
+ const enabledElement = getEnabledElement(this._previewData.element);
152
+ if (!enabledElement) {
142
153
  return;
143
154
  }
144
- this._previewData.preview = this.applyActiveStrategyCallback(getEnabledElement(this._previewData.element), this.getOperationData(this._previewData.element), StrategyCallbacks.Preview);
155
+ const { viewport } = enabledElement;
156
+ const activeStrategy = this.configuration.activeStrategy;
157
+ const strategyData = getStrategyData({
158
+ operationData,
159
+ viewport,
160
+ strategy: activeStrategy,
161
+ });
162
+ if (!operationData) {
163
+ return;
164
+ }
165
+ const memo = this.createMemo(operationData.segmentationId, strategyData.segmentationVoxelManager);
166
+ this._previewData.preview = this.applyActiveStrategyCallback(getEnabledElement(this._previewData.element), {
167
+ ...operationData,
168
+ ...strategyData,
169
+ memo,
170
+ }, StrategyCallbacks.Preview);
145
171
  };
146
172
  this._dragCallback = (evt) => {
147
173
  const eventData = evt.detail;
@@ -153,11 +179,14 @@ class BrushTool extends LabelmapBaseTool {
153
179
  const delta = vec2.distance(currentPoints.canvas, this._previewData.startPoint);
154
180
  const { dragTimeMs, dragMoveDistance } = this.configuration.preview;
155
181
  if (!this._previewData.isDrag &&
156
- this._previewData.preview &&
157
182
  Date.now() - this._previewData.timerStart < dragTimeMs &&
158
183
  delta < dragMoveDistance) {
159
184
  return;
160
185
  }
186
+ if (this._previewData.timer) {
187
+ window.clearTimeout(this._previewData.timer);
188
+ this._previewData.timer = null;
189
+ }
161
190
  this._previewData.preview = this.applyActiveStrategy(enabledElement, this.getOperationData(element));
162
191
  this._previewData.element = element;
163
192
  this._previewData.timerStart = Date.now() + dragTimeMs;
@@ -171,8 +200,8 @@ class BrushTool extends LabelmapBaseTool {
171
200
  const operationData = this.getOperationData(element);
172
201
  if (!this._previewData.preview && !this._previewData.isDrag) {
173
202
  this.applyActiveStrategy(enabledElement, operationData);
203
+ this.doneEditMemo();
174
204
  }
175
- this.doneEditMemo();
176
205
  this._deactivateDraw(element);
177
206
  resetElementCursor(element);
178
207
  this.updateCursor(evt);
@@ -266,30 +295,28 @@ class BrushTool extends LabelmapBaseTool {
266
295
  return stats;
267
296
  }
268
297
  rejectPreview(element = this._previewData.element) {
269
- if (!element || !this._previewData.preview) {
298
+ if (!element) {
270
299
  return;
271
300
  }
301
+ this.doneEditMemo();
272
302
  const enabledElement = getEnabledElement(element);
273
303
  this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.RejectPreview);
274
304
  this._previewData.preview = null;
275
305
  this._previewData.isDrag = false;
276
306
  }
277
- interpolate(element, config) {
307
+ acceptPreview(element = this._previewData.element) {
278
308
  if (!element) {
279
309
  return;
280
310
  }
281
- const enabledElement = getEnabledElement(element);
282
- this._previewData.preview = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.Interpolate, config.configuration);
283
- this._previewData.isDrag = true;
311
+ super.acceptPreview(element);
284
312
  }
285
- acceptPreview(element = this._previewData.element) {
313
+ interpolate(element, config) {
286
314
  if (!element) {
287
315
  return;
288
316
  }
289
317
  const enabledElement = getEnabledElement(element);
290
- this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AcceptPreview);
291
- this._previewData.isDrag = false;
292
- this._previewData.preview = null;
318
+ this._previewData.preview = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.Interpolate, config.configuration);
319
+ this._previewData.isDrag = true;
293
320
  }
294
321
  invalidateBrushCursor() {
295
322
  if (this._hoverData === undefined) {
@@ -339,6 +366,7 @@ class BrushTool extends LabelmapBaseTool {
339
366
  const circleUID = '0';
340
367
  drawCircleSvg(svgDrawingHelper, annotationUID, circleUID, center, radius, {
341
368
  color,
369
+ lineDash: this.centerSegmentIndexInfo.segmentIndex === 0 ? [1, 2] : null,
342
370
  });
343
371
  const { dynamicRadiusInCanvas } = this.configuration?.threshold || {
344
372
  dynamicRadiusInCanvas: 0,
@@ -42,6 +42,12 @@ export default class LabelmapBaseTool extends BaseTool {
42
42
  volumeId?: string;
43
43
  referencedVolumeId?: string;
44
44
  } | null;
45
+ protected centerSegmentIndexInfo: {
46
+ segmentIndex: number;
47
+ hasSegmentIndex: boolean;
48
+ hasPreviewIndex: boolean;
49
+ changedIndices: number[];
50
+ };
45
51
  protected _hoverData?: {
46
52
  brushCursor: any;
47
53
  segmentationId: string;
@@ -52,9 +58,16 @@ export default class LabelmapBaseTool extends BaseTool {
52
58
  viewport: Types.IViewport;
53
59
  };
54
60
  static previewData?: PreviewData;
61
+ protected memoMap: Map<string, LabelmapMemo.LabelmapMemo>;
62
+ protected acceptedMemoIds: Map<string, {
63
+ element: HTMLDivElement;
64
+ segmentIndex: number;
65
+ }>;
66
+ protected memo: LabelmapMemo.LabelmapMemo;
55
67
  constructor(toolProps: any, defaultToolProps: any);
68
+ protected _historyRedoHandler(evt: any): void;
56
69
  protected get _previewData(): PreviewData;
57
- createMemo(segmentId: string, segmentationVoxelManager: any, preview: any): LabelmapMemo.LabelmapMemo;
70
+ createMemo(segmentationId: string, segmentationVoxelManager: any): LabelmapMemo.LabelmapMemo;
58
71
  protected createEditData(element: any): EditDataReturnType;
59
72
  protected getEditData({ viewport, representationData, segmentsLocked, segmentationId, }: {
60
73
  viewport: any;
@@ -89,7 +102,7 @@ export default class LabelmapBaseTool extends BaseTool {
89
102
  protected getOperationData(element?: any): ModifiedLabelmapToolOperationData;
90
103
  addPreview(element?: HTMLDivElement, options?: {
91
104
  acceptReject: boolean;
92
- }): unknown;
105
+ }): any;
93
106
  rejectPreview(element?: HTMLDivElement): void;
94
107
  acceptPreview(element?: HTMLDivElement): void;
95
108
  static viewportContoursToLabelmap(viewport: Types.IViewport, options?: {
@@ -8,7 +8,7 @@ import { getClosestImageIdForStackViewport } from '../../utilities/annotationHyd
8
8
  import { getCurrentLabelmapImageIdForViewport } from '../../stateManagement/segmentation/getCurrentLabelmapImageIdForViewport';
9
9
  import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
10
10
  import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
11
- import { StrategyCallbacks } from '../../enums';
11
+ import { StrategyCallbacks, Events } from '../../enums';
12
12
  import * as LabelmapMemo from '../../utilities/segmentation/createLabelmapMemo';
13
13
  import { getAllAnnotations, removeAnnotation, } from '../../stateManagement/annotation/annotationState';
14
14
  import { filterAnnotationsForDisplay } from '../../utilities/planar';
@@ -26,13 +26,54 @@ export default class LabelmapBaseTool extends BaseTool {
26
26
  }; }
27
27
  constructor(toolProps, defaultToolProps) {
28
28
  super(toolProps, defaultToolProps);
29
+ this.memoMap = new Map();
30
+ this.acceptedMemoIds = new Map();
31
+ this.centerSegmentIndexInfo = {
32
+ segmentIndex: null,
33
+ hasSegmentIndex: false,
34
+ hasPreviewIndex: false,
35
+ changedIndices: [],
36
+ };
37
+ }
38
+ _historyRedoHandler(evt) {
39
+ const { id, operationType } = evt.detail;
40
+ if (operationType !== 'labelmap') {
41
+ return;
42
+ }
43
+ if (this.acceptedMemoIds.has(id)) {
44
+ this._hoverData = null;
45
+ const memoData = this.acceptedMemoIds.get(id);
46
+ const element = memoData?.element;
47
+ const operationData = this.getOperationData(element);
48
+ operationData.segmentIndex = memoData?.segmentIndex;
49
+ if (element) {
50
+ this.applyActiveStrategyCallback(getEnabledElement(element), operationData, StrategyCallbacks.AcceptPreview);
51
+ }
52
+ }
53
+ this._previewData.isDrag = true;
29
54
  }
30
55
  get _previewData() {
31
56
  return LabelmapBaseTool.previewData;
32
57
  }
33
- createMemo(segmentId, segmentationVoxelManager, preview) {
34
- this.memo ||= LabelmapMemo.createLabelmapMemo(segmentId, segmentationVoxelManager, preview);
35
- return this.memo;
58
+ createMemo(segmentationId, segmentationVoxelManager) {
59
+ const voxelManagerId = segmentationVoxelManager.id;
60
+ if (this.memo &&
61
+ this.memo.segmentationVoxelManager === segmentationVoxelManager) {
62
+ return this.memo;
63
+ }
64
+ let memo = this.memoMap.get(voxelManagerId);
65
+ if (!memo) {
66
+ memo = LabelmapMemo.createLabelmapMemo(segmentationId, segmentationVoxelManager);
67
+ this.memoMap.set(voxelManagerId, memo);
68
+ }
69
+ else {
70
+ if (memo.redoVoxelManager) {
71
+ memo = LabelmapMemo.createLabelmapMemo(segmentationId, segmentationVoxelManager);
72
+ this.memoMap.set(voxelManagerId, memo);
73
+ }
74
+ }
75
+ this.memo = memo;
76
+ return memo;
36
77
  }
37
78
  createEditData(element) {
38
79
  const enabledElement = getEnabledElement(element);
@@ -154,20 +195,25 @@ export default class LabelmapBaseTool extends BaseTool {
154
195
  if (!configColor && !segmentColor) {
155
196
  return;
156
197
  }
198
+ let previewColor = null, previewSegmentIndex = null;
199
+ if (this.configuration.preview.enabled) {
200
+ previewColor = configColor || lightenColor(...segmentColor);
201
+ previewSegmentIndex = 255;
202
+ }
157
203
  const operationData = {
158
204
  ...editData,
159
205
  points: data?.handles?.points,
160
206
  segmentIndex,
161
207
  viewPlaneNormal,
208
+ previewOnHover: !this._previewData.isDrag,
162
209
  toolGroupId: this.toolGroupId,
163
210
  segmentationId,
164
211
  viewUp,
212
+ centerSegmentIndexInfo: this.centerSegmentIndexInfo,
165
213
  activeStrategy: this.configuration.activeStrategy,
166
214
  configuration: this.configuration,
167
- previewColor: this.configuration.preview.enabled
168
- ? configColor || lightenColor(...segmentColor)
169
- : null,
170
- preview: this._previewData?.preview,
215
+ previewColor,
216
+ previewSegmentIndex,
171
217
  createMemo: this.createMemo.bind(this),
172
218
  };
173
219
  return operationData;
@@ -182,14 +228,15 @@ export default class LabelmapBaseTool extends BaseTool {
182
228
  this.rejectPreview(element);
183
229
  }
184
230
  const enabledElement = getEnabledElement(element);
185
- _previewData.preview = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AddPreview);
231
+ const results = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AddPreview);
186
232
  _previewData.isDrag = true;
187
- return _previewData.preview;
233
+ return results;
188
234
  }
189
235
  rejectPreview(element = this._previewData.element) {
190
- if (!element || !this._previewData.preview) {
236
+ if (!element) {
191
237
  return;
192
238
  }
239
+ this.doneEditMemo();
193
240
  const enabledElement = getEnabledElement(element);
194
241
  this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.RejectPreview);
195
242
  this._previewData.preview = null;
@@ -199,12 +246,18 @@ export default class LabelmapBaseTool extends BaseTool {
199
246
  if (!element) {
200
247
  return;
201
248
  }
202
- this.doneEditMemo();
249
+ const operationData = this.getOperationData(element);
250
+ if (this.memo && this.memo.id) {
251
+ this.acceptedMemoIds.set(this.memo.id, {
252
+ element,
253
+ segmentIndex: operationData.segmentIndex,
254
+ });
255
+ }
203
256
  const enabledElement = getEnabledElement(element);
204
- this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AcceptPreview);
205
- this._previewData.isDrag = false;
206
- this._previewData.preview = null;
257
+ this.applyActiveStrategyCallback(enabledElement, operationData, StrategyCallbacks.AcceptPreview);
207
258
  this.doneEditMemo();
259
+ this._previewData.preview = null;
260
+ this._previewData.isDrag = false;
208
261
  }
209
262
  static viewportContoursToLabelmap(viewport, options) {
210
263
  const removeContours = options?.removeContours ?? true;
@@ -227,7 +280,7 @@ export default class LabelmapBaseTool extends BaseTool {
227
280
  });
228
281
  const preview = brushInstance.addPreview(viewport.element);
229
282
  const { memo, segmentationId } = preview;
230
- const previewVoxels = memo?.voxelManager || preview.previewVoxelManager;
283
+ const previewVoxels = memo?.voxelManager;
231
284
  const segmentationVoxels = previewVoxels.sourceVoxelManager || previewVoxels;
232
285
  const { dimensions } = previewVoxels;
233
286
  const imageData = viewport
@@ -5,6 +5,12 @@ import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
5
5
  import type { LabelmapMemo } from '../../../utilities/segmentation/createLabelmapMemo';
6
6
  export type InitializedOperationData = LabelmapToolOperationDataAny & {
7
7
  operationName?: string;
8
+ centerSegmentIndexInfo: {
9
+ segmentIndex: number;
10
+ hasSegmentIndex: boolean;
11
+ hasPreviewIndex: boolean;
12
+ changedIndices: number[];
13
+ };
8
14
  enabledElement: Types.IEnabledElement;
9
15
  centerIJK?: Types.Point3;
10
16
  centerWorld: Types.Point3;
@@ -14,7 +20,6 @@ export type InitializedOperationData = LabelmapToolOperationDataAny & {
14
20
  imageVoxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
15
21
  segmentationVoxelManager: Types.IVoxelManager<number>;
16
22
  segmentationImageData: vtkImageData;
17
- previewVoxelManager: Types.IVoxelManager<number>;
18
23
  previewSegmentIndex?: number;
19
24
  previewColor?: [number, number, number, number];
20
25
  brushStrategy: BrushStrategy;
@@ -42,7 +47,6 @@ export type Composition = CompositionFunction | CompositionInstance;
42
47
  export default class BrushStrategy {
43
48
  static COMPOSITIONS: {
44
49
  determineSegmentIndex: {
45
- initialize: (operationData: InitializedOperationData) => void;
46
50
  onInteractionStart: (operationData: InitializedOperationData) => void;
47
51
  };
48
52
  dynamicThreshold: {
@@ -112,12 +116,12 @@ export default class BrushStrategy {
112
116
  protected _acceptPreview: [];
113
117
  protected _onInteractionStart: any[];
114
118
  constructor(name: any, ...initializers: Composition[]);
115
- fill: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => any;
119
+ fill: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => InitializedOperationData;
116
120
  protected initialize(enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny, operationName?: string): InitializedOperationData;
117
121
  onInteractionStart: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
118
122
  onInteractionEnd: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
119
123
  rejectPreview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
120
- addPreview: (enabledElement: any, operationData: LabelmapToolOperationDataAny) => any;
124
+ addPreview: (enabledElement: any, operationData: LabelmapToolOperationDataAny) => InitializedOperationData;
121
125
  acceptPreview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
122
126
  preview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => unknown;
123
127
  interpolate: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => unknown;
@@ -3,7 +3,6 @@ import { triggerSegmentationDataModified } from '../../../stateManagement/segmen
3
3
  import compositions from './compositions';
4
4
  import { getStrategyData } from './utils/getStrategyData';
5
5
  import { StrategyCallbacks } from '../../../enums';
6
- const { VoxelManager } = csUtils;
7
6
  export default class BrushStrategy {
8
7
  static { this.COMPOSITIONS = compositions; }
9
8
  static { this.childFunctions = {
@@ -33,29 +32,12 @@ export default class BrushStrategy {
33
32
  if (!initializedData) {
34
33
  return;
35
34
  }
36
- const { configuration = {}, centerIJK } = initializedData;
37
- if (csUtils.isEqual(centerIJK, configuration.centerIJK)) {
38
- return operationData.preview;
39
- }
40
- else {
41
- configuration.centerIJK = centerIJK;
42
- }
43
35
  this._fill.forEach((func) => func(initializedData));
44
- const { segmentationVoxelManager, previewVoxelManager, previewSegmentIndex, segmentIndex, } = initializedData;
45
- const isPreview = previewSegmentIndex && previewVoxelManager.modifiedSlices.size;
46
- triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices(), isPreview ? previewSegmentIndex : segmentIndex);
47
- if (!previewSegmentIndex || !previewVoxelManager.modifiedSlices.size) {
48
- segmentationVoxelManager.resetModifiedSlices();
49
- return null;
50
- }
51
- return initializedData.preview || initializedData;
36
+ const { segmentationVoxelManager, segmentIndex } = initializedData;
37
+ triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices(), segmentIndex);
38
+ return initializedData;
52
39
  };
53
40
  this.onInteractionStart = (enabledElement, operationData) => {
54
- const { preview } = operationData;
55
- if (preview?.isPreviewFromHover) {
56
- preview.isPreviewFromHover = false;
57
- return;
58
- }
59
41
  const initializedData = this.initialize(enabledElement, operationData);
60
42
  if (!initializedData) {
61
43
  return;
@@ -67,7 +49,7 @@ export default class BrushStrategy {
67
49
  if (!initializedData) {
68
50
  return;
69
51
  }
70
- return initializedData.preview || initializedData;
52
+ return initializedData;
71
53
  };
72
54
  this.configurationName = name;
73
55
  this.compositions = initializers;
@@ -94,28 +76,24 @@ export default class BrushStrategy {
94
76
  const { viewport } = enabledElement;
95
77
  const data = getStrategyData({ operationData, viewport, strategy: this });
96
78
  if (!data) {
97
- console.warn('No data found for BrushStrategy');
98
- return operationData.preview;
79
+ return null;
99
80
  }
100
81
  const { imageVoxelManager, segmentationVoxelManager, segmentationImageData, } = data;
101
- const previewVoxelManager = operationData.preview?.previewVoxelManager ||
102
- VoxelManager.createRLEHistoryVoxelManager(segmentationVoxelManager);
103
- const previewEnabled = !!operationData.previewColor;
104
- const previewSegmentIndex = previewEnabled ? 255 : undefined;
82
+ const memo = operationData.createMemo(operationData.segmentationId, segmentationVoxelManager);
105
83
  const initializedData = {
106
84
  operationName,
107
- previewSegmentIndex,
108
85
  ...operationData,
86
+ segmentIndex: operationData.segmentIndex,
109
87
  enabledElement,
110
88
  imageVoxelManager,
111
89
  segmentationVoxelManager,
112
90
  segmentationImageData,
113
- previewVoxelManager,
114
91
  viewport,
115
92
  centerWorld: null,
116
93
  isInObject: null,
117
94
  isInObjectBoundsIJK: null,
118
95
  brushStrategy: this,
96
+ memo,
119
97
  };
120
98
  this._initialize.forEach((func) => func(initializedData));
121
99
  return initializedData;
@@ -1,6 +1,5 @@
1
1
  import type { InitializedOperationData } from '../BrushStrategy';
2
2
  declare const _default: {
3
- initialize: (operationData: InitializedOperationData) => void;
4
3
  onInteractionStart: (operationData: InitializedOperationData) => void;
5
4
  };
6
5
  export default _default;
@@ -1,18 +1,10 @@
1
1
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
2
2
  export default {
3
- [StrategyCallbacks.Initialize]: (operationData) => {
4
- const { centerSegmentIndex } = operationData.configuration || {};
5
- if (!centerSegmentIndex) {
6
- return;
7
- }
8
- operationData.segmentIndex = centerSegmentIndex.segmentIndex;
9
- },
10
3
  [StrategyCallbacks.OnInteractionStart]: (operationData) => {
11
- const { segmentIndex, previewSegmentIndex, segmentationVoxelManager, centerIJK, viewPlaneNormal, segmentationImageData, preview, configuration, } = operationData;
4
+ const { segmentIndex, previewSegmentIndex, segmentationVoxelManager, centerIJK, viewPlaneNormal, segmentationImageData, configuration, } = operationData;
12
5
  if (!configuration?.useCenterSegmentIndex) {
13
6
  return;
14
7
  }
15
- delete configuration.centerSegmentIndex;
16
8
  let hasSegmentIndex = false;
17
9
  let hasPreviewIndex = false;
18
10
  const nestedBounds = [
@@ -37,23 +29,12 @@ export default {
37
29
  boundsIJK: nestedBounds,
38
30
  });
39
31
  if (!hasSegmentIndex && !hasPreviewIndex) {
32
+ operationData.centerSegmentIndexInfo.segmentIndex = null;
40
33
  return;
41
34
  }
42
- let existingValue = segmentationVoxelManager.getAtIJKPoint(centerIJK);
43
- if (existingValue === previewSegmentIndex) {
44
- if (preview) {
45
- existingValue = preview.segmentIndex;
46
- }
47
- else {
48
- return;
49
- }
50
- }
51
- else if (hasPreviewIndex) {
52
- existingValue = null;
53
- }
54
- operationData.segmentIndex = existingValue;
55
- configuration.centerSegmentIndex = {
56
- segmentIndex: existingValue,
57
- };
35
+ const existingValue = segmentationVoxelManager.getAtIJKPoint(centerIJK);
36
+ operationData.centerSegmentIndexInfo.segmentIndex = existingValue;
37
+ operationData.centerSegmentIndexInfo.hasSegmentIndex = hasSegmentIndex;
38
+ operationData.centerSegmentIndexInfo.hasPreviewIndex = hasPreviewIndex;
58
39
  },
59
40
  };
@@ -1,6 +1,5 @@
1
1
  declare const _default: {
2
2
  determineSegmentIndex: {
3
- initialize: (operationData: import("../BrushStrategy").InitializedOperationData) => void;
4
3
  onInteractionStart: (operationData: import("../BrushStrategy").InitializedOperationData) => void;
5
4
  };
6
5
  dynamicThreshold: {
@@ -3,13 +3,13 @@ import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
3
3
  import IslandRemoval from '../../../../utilities/segmentation/islandRemoval';
4
4
  export default {
5
5
  [StrategyCallbacks.OnInteractionEnd]: (operationData) => {
6
- const { previewSegmentIndex, segmentIndex, viewport, previewVoxelManager, segmentationVoxelManager, activeStrategy, } = operationData;
6
+ const { previewSegmentIndex, segmentIndex, viewport, segmentationVoxelManager, activeStrategy, memo, } = operationData;
7
7
  if (activeStrategy !== 'THRESHOLD_INSIDE_SPHERE_WITH_ISLAND_REMOVAL' ||
8
8
  segmentIndex === null) {
9
9
  return;
10
10
  }
11
11
  const islandRemoval = new IslandRemoval();
12
- const voxelManager = previewVoxelManager ?? segmentationVoxelManager;
12
+ const voxelManager = memo?.voxelManager || segmentationVoxelManager;
13
13
  if (!islandRemoval.initialize(viewport, voxelManager, {
14
14
  previewSegmentIndex,
15
15
  segmentIndex,
@@ -1,38 +1,24 @@
1
+ import { utilities } from '@cornerstonejs/core';
1
2
  import { triggerSegmentationDataModified } from '../../../../stateManagement/segmentation/events/triggerSegmentationDataModified';
2
3
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
3
- import { getSegmentIndexColor, setSegmentIndexColor, } from '../../../../stateManagement/segmentation/config/segmentationColor';
4
+ import { setSegmentIndexColor } from '../../../../stateManagement/segmentation/config/segmentationColor';
4
5
  import { getViewportIdsWithSegmentation } from '../../../../stateManagement/segmentation/getViewportIdsWithSegmentation';
5
6
  export default {
6
7
  [StrategyCallbacks.Preview]: function (operationData) {
7
- const { previewColor, configuration, enabledElement } = operationData;
8
- if (!previewColor || !configuration) {
8
+ const { previewSegmentIndex, configuration, enabledElement } = operationData;
9
+ if (!previewSegmentIndex || !configuration) {
9
10
  return;
10
11
  }
11
- if (operationData.preview) {
12
- delete operationData.preview;
13
- }
14
- delete configuration.centerSegmentIndex;
15
12
  this.onInteractionStart?.(enabledElement, operationData);
16
13
  const preview = this.fill(enabledElement, operationData);
17
14
  if (preview) {
18
- preview.isPreviewFromHover = true;
19
- operationData.preview = preview;
20
15
  this.onInteractionEnd?.(enabledElement, operationData);
21
16
  }
22
17
  return preview;
23
18
  },
24
19
  [StrategyCallbacks.Initialize]: (operationData) => {
25
- const { segmentIndex, previewSegmentIndex, previewColor, preview, segmentationId, segmentationVoxelManager, } = operationData;
26
- if (previewColor === undefined || !previewSegmentIndex) {
27
- operationData.memo = operationData.createMemo(segmentationId, segmentationVoxelManager);
28
- return;
29
- }
30
- if (preview) {
31
- preview.previewVoxelManager.sourceVoxelManager =
32
- operationData.segmentationVoxelManager;
33
- operationData.previewVoxelManager = preview.previewVoxelManager;
34
- }
35
- if (segmentIndex === null) {
20
+ const { segmentIndex, previewColor, previewSegmentIndex } = operationData;
21
+ if (previewSegmentIndex == null || segmentIndex == null) {
36
22
  return;
37
23
  }
38
24
  const viewportIds = getViewportIdsWithSegmentation(operationData.segmentationId);
@@ -41,38 +27,44 @@ export default {
41
27
  });
42
28
  },
43
29
  [StrategyCallbacks.AcceptPreview]: (operationData) => {
44
- const { segmentationVoxelManager, previewVoxelManager: previewVoxelManager, previewSegmentIndex, segmentationId, preview, } = operationData || {};
45
- if (previewSegmentIndex === undefined) {
46
- return;
47
- }
48
- const segmentIndex = preview?.segmentIndex ?? operationData.segmentIndex;
49
- if (!previewVoxelManager || previewVoxelManager.modifiedSlices.size === 0) {
50
- return;
51
- }
52
- const memo = operationData.createMemo(segmentationId, segmentationVoxelManager);
53
- operationData.memo = memo;
54
- const { voxelManager } = memo;
55
- const callback = ({ index, value }) => {
30
+ const { previewSegmentIndex, segmentationVoxelManager, memo, segmentIndex, centerSegmentIndexInfo, } = operationData || {};
31
+ const { changedIndices } = centerSegmentIndexInfo || {};
32
+ const labelmapMemo = memo;
33
+ const callback = ({ index }) => {
56
34
  const oldValue = segmentationVoxelManager.getAtIndex(index);
57
- if (oldValue === previewSegmentIndex) {
58
- segmentationVoxelManager.setAtIndex(index, value);
59
- voxelManager.setAtIndex(index, segmentIndex);
35
+ if (changedIndices?.length > 0) {
36
+ if (changedIndices.includes(index)) {
37
+ labelmapMemo.voxelManager.setAtIndex(index, 0);
38
+ }
39
+ }
40
+ else {
41
+ if (oldValue === previewSegmentIndex) {
42
+ labelmapMemo.voxelManager.setAtIndex(index, segmentIndex);
43
+ }
60
44
  }
61
45
  };
62
- previewVoxelManager.forEach(callback, {});
63
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices(), preview.segmentIndex);
64
- previewVoxelManager.clear();
46
+ segmentationVoxelManager.forEach(callback);
47
+ triggerSegmentationDataModified(operationData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices(), segmentIndex);
48
+ operationData.centerSegmentIndexInfo.changedIndices = [];
65
49
  },
66
50
  [StrategyCallbacks.RejectPreview]: (operationData) => {
67
- const { previewVoxelManager: previewVoxelManager, segmentationVoxelManager, } = operationData;
68
- if (previewVoxelManager.modifiedSlices.size === 0) {
51
+ if (!operationData) {
69
52
  return;
70
53
  }
71
- const callback = ({ index, value }) => {
72
- segmentationVoxelManager.setAtIndex(index, value);
73
- };
74
- previewVoxelManager.forEach(callback);
75
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices(), 0);
76
- previewVoxelManager.clear();
54
+ utilities.HistoryMemo.DefaultHistoryMemo.undoIf((memo) => {
55
+ const labelmapMemo = memo;
56
+ if (!labelmapMemo?.voxelManager) {
57
+ return false;
58
+ }
59
+ const { segmentationVoxelManager } = labelmapMemo;
60
+ let hasPreviewSegmentIndex = false;
61
+ const callback = ({ value }) => {
62
+ if (value === operationData.previewSegmentIndex) {
63
+ hasPreviewSegmentIndex = true;
64
+ }
65
+ };
66
+ segmentationVoxelManager.forEach(callback);
67
+ return hasPreviewSegmentIndex;
68
+ });
77
69
  },
78
70
  };
@@ -1,7 +1,7 @@
1
1
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
2
2
  export default {
3
3
  [StrategyCallbacks.Fill]: (operationData) => {
4
- const { segmentsLocked, segmentationImageData, segmentationVoxelManager, previewVoxelManager, brushStrategy, centerIJK, } = operationData;
4
+ const { segmentsLocked, segmentationImageData, segmentationVoxelManager, brushStrategy, centerIJK, } = operationData;
5
5
  const isWithinThreshold = brushStrategy.createIsInThreshold?.(operationData);
6
6
  const { setValue } = brushStrategy;
7
7
  const callback = isWithinThreshold
@@ -18,6 +18,6 @@ export default {
18
18
  isInObject: operationData.isInObject,
19
19
  boundsIJK: operationData.isInObjectBoundsIJK,
20
20
  });
21
- previewVoxelManager.addPoint(centerIJK);
21
+ segmentationVoxelManager.addPoint(centerIJK);
22
22
  },
23
23
  };
@@ -1,29 +1,35 @@
1
1
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
2
+ import { handleUseSegmentCenterIndex } from '../utils/handleUseSegmentCenterIndex';
2
3
  export default {
3
4
  [StrategyCallbacks.INTERNAL_setValue]: (operationData, { value, index }) => {
4
- const { segmentsLocked, segmentIndex, previewSegmentIndex, segmentationVoxelManager, memo, } = operationData;
5
- const previewVoxelManager = memo?.voxelManager || operationData.previewVoxelManager;
5
+ const { segmentsLocked, previewSegmentIndex, memo, segmentationVoxelManager, centerSegmentIndexInfo, segmentIndex, } = operationData;
6
6
  const existingValue = segmentationVoxelManager.getAtIndex(index);
7
- let changed = false;
8
- if (segmentIndex === null) {
9
- const oldValue = previewVoxelManager.getAtIndex(index);
10
- if (oldValue !== undefined) {
11
- changed = previewVoxelManager.setAtIndex(index, oldValue);
12
- }
7
+ if (segmentsLocked.includes(value)) {
13
8
  return;
14
9
  }
15
- if (existingValue === segmentIndex || segmentsLocked.includes(value)) {
10
+ if (!centerSegmentIndexInfo && existingValue === segmentIndex) {
16
11
  return;
17
12
  }
18
- if (existingValue === previewSegmentIndex) {
19
- if (previewVoxelManager.getAtIndex(index) === undefined) {
20
- changed = segmentationVoxelManager.setAtIndex(index, segmentIndex);
21
- }
22
- else {
23
- return;
13
+ if (centerSegmentIndexInfo?.segmentIndex !== 0 &&
14
+ existingValue === segmentIndex) {
15
+ return;
16
+ }
17
+ if (centerSegmentIndexInfo?.segmentIndex === null) {
18
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex ?? segmentIndex);
19
+ return;
20
+ }
21
+ if (!previewSegmentIndex) {
22
+ let useSegmentIndex = segmentIndex;
23
+ if (centerSegmentIndexInfo) {
24
+ useSegmentIndex = centerSegmentIndexInfo.segmentIndex;
24
25
  }
26
+ memo.voxelManager.setAtIndex(index, useSegmentIndex);
27
+ return;
25
28
  }
26
- const useSegmentIndex = previewSegmentIndex ?? segmentIndex;
27
- changed = previewVoxelManager.setAtIndex(index, useSegmentIndex);
29
+ handleUseSegmentCenterIndex({
30
+ operationData,
31
+ existingValue,
32
+ index,
33
+ });
28
34
  },
29
35
  };
@@ -97,6 +97,9 @@ function getStrategyDataForStackViewport({ operationData, viewport, strategy, })
97
97
  };
98
98
  }
99
99
  function getStrategyData({ operationData, viewport, strategy, }) {
100
+ if (!operationData) {
101
+ return null;
102
+ }
100
103
  if (('volumeId' in operationData && operationData.volumeId != null) ||
101
104
  ('referencedVolumeId' in operationData &&
102
105
  operationData.referencedVolumeId != null)) {
@@ -0,0 +1,5 @@
1
+ export declare function handleUseSegmentCenterIndex({ operationData, existingValue, index, }: {
2
+ operationData: any;
3
+ existingValue: any;
4
+ index: any;
5
+ }): void;
@@ -0,0 +1,84 @@
1
+ export function handleUseSegmentCenterIndex({ operationData, existingValue, index, }) {
2
+ const { previewSegmentIndex, memo, centerSegmentIndexInfo, previewOnHover, segmentIndex, } = operationData;
3
+ const { hasPreviewIndex, hasSegmentIndex, segmentIndex: centerSegmentIndex, } = centerSegmentIndexInfo;
4
+ if (centerSegmentIndex === 0 && hasSegmentIndex && hasPreviewIndex) {
5
+ if (existingValue === segmentIndex) {
6
+ return;
7
+ }
8
+ if (previewOnHover) {
9
+ return;
10
+ }
11
+ if (existingValue === previewSegmentIndex) {
12
+ memo.voxelManager.setAtIndex(index, 0);
13
+ return;
14
+ }
15
+ return;
16
+ }
17
+ if (centerSegmentIndex === 0 && hasSegmentIndex && !hasPreviewIndex) {
18
+ if (existingValue === 0 || existingValue !== segmentIndex) {
19
+ return;
20
+ }
21
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
22
+ centerSegmentIndexInfo.changedIndices.push(index);
23
+ return;
24
+ }
25
+ if (centerSegmentIndex === 0 && !hasSegmentIndex && hasPreviewIndex) {
26
+ if (existingValue === segmentIndex) {
27
+ return;
28
+ }
29
+ if (previewOnHover) {
30
+ return;
31
+ }
32
+ if (existingValue === previewSegmentIndex) {
33
+ memo.voxelManager.setAtIndex(index, 0);
34
+ return;
35
+ }
36
+ return;
37
+ }
38
+ if (centerSegmentIndex === 0 && !hasSegmentIndex && !hasPreviewIndex) {
39
+ if (existingValue === segmentIndex) {
40
+ return;
41
+ }
42
+ if (existingValue === previewSegmentIndex) {
43
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
44
+ return;
45
+ }
46
+ return;
47
+ }
48
+ if (centerSegmentIndex === previewSegmentIndex &&
49
+ hasSegmentIndex &&
50
+ hasPreviewIndex) {
51
+ if (existingValue === segmentIndex) {
52
+ return;
53
+ }
54
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
55
+ return;
56
+ }
57
+ if (centerSegmentIndex === previewSegmentIndex &&
58
+ !hasSegmentIndex &&
59
+ hasPreviewIndex) {
60
+ if (existingValue === segmentIndex) {
61
+ return;
62
+ }
63
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
64
+ return;
65
+ }
66
+ if (centerSegmentIndex === segmentIndex &&
67
+ hasSegmentIndex &&
68
+ hasPreviewIndex) {
69
+ if (existingValue === segmentIndex) {
70
+ return;
71
+ }
72
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
73
+ return;
74
+ }
75
+ if (centerSegmentIndex === segmentIndex &&
76
+ hasSegmentIndex &&
77
+ !hasPreviewIndex) {
78
+ if (existingValue === segmentIndex) {
79
+ return;
80
+ }
81
+ memo.voxelManager.setAtIndex(index, previewSegmentIndex);
82
+ return;
83
+ }
84
+ }
@@ -6,6 +6,7 @@ type LabelmapToolOperationData = {
6
6
  segmentationId: string;
7
7
  segmentIndex: number;
8
8
  previewColor?: [number, number, number, number];
9
+ previewSegmentIndex?: number;
9
10
  segmentsLocked: number[];
10
11
  viewPlaneNormal: number[];
11
12
  viewUp: number[];
@@ -16,7 +17,6 @@ type LabelmapToolOperationData = {
16
17
  voxelManager: Types.IVoxelManager<number>;
17
18
  imageData: vtkImageData;
18
19
  };
19
- preview: any;
20
20
  toolGroupId: string;
21
21
  createMemo: (segmentId: any, segmentVoxels: any, previewVoxels?: any, previewMemo?: any) => LabelmapMemo;
22
22
  };
@@ -1,27 +1,21 @@
1
1
  import { utilities } from '@cornerstonejs/core';
2
2
  import type { Types } from '@cornerstonejs/core';
3
- import type { InitializedOperationData } from '../../tools/segmentation/strategies/BrushStrategy';
4
3
  export type LabelmapMemo = Types.Memo & {
5
4
  segmentationVoxelManager: Types.IVoxelManager<number>;
6
5
  voxelManager: Types.IVoxelManager<number>;
7
6
  redoVoxelManager?: Types.IVoxelManager<number>;
8
7
  undoVoxelManager?: Types.IVoxelManager<number>;
9
8
  memo?: LabelmapMemo;
9
+ id: string;
10
10
  };
11
- export declare function createLabelmapMemo<T>(segmentationId: string, segmentationVoxelManager: Types.IVoxelManager<T>, preview?: InitializedOperationData): {
12
- segmentationId: string;
13
- restoreMemo: typeof restoreMemo;
14
- commitMemo: typeof commitMemo;
15
- segmentationVoxelManager: Types.IVoxelManager<number>;
16
- voxelManager: Types.IVoxelManager<number>;
17
- memo: LabelmapMemo;
18
- preview: InitializedOperationData;
19
- } | {
11
+ export declare function createLabelmapMemo<T>(segmentationId: string, segmentationVoxelManager: Types.IVoxelManager<T>): {
20
12
  segmentationId: string;
21
13
  restoreMemo: typeof restoreMemo;
22
14
  commitMemo: typeof commitMemo;
23
15
  segmentationVoxelManager: Types.IVoxelManager<T>;
24
16
  voxelManager: utilities.VoxelManager<T>;
17
+ id: string;
18
+ operationType: string;
25
19
  };
26
20
  export declare function restoreMemo(isUndo?: boolean): void;
27
21
  export declare function createRleMemo<T>(segmentationId: string, segmentationVoxelManager: Types.IVoxelManager<T>): {
@@ -30,15 +24,8 @@ export declare function createRleMemo<T>(segmentationId: string, segmentationVox
30
24
  commitMemo: typeof commitMemo;
31
25
  segmentationVoxelManager: Types.IVoxelManager<T>;
32
26
  voxelManager: utilities.VoxelManager<T>;
33
- };
34
- export declare function createPreviewMemo(segmentationId: string, preview: InitializedOperationData): {
35
- segmentationId: string;
36
- restoreMemo: typeof restoreMemo;
37
- commitMemo: typeof commitMemo;
38
- segmentationVoxelManager: Types.IVoxelManager<number>;
39
- voxelManager: Types.IVoxelManager<number>;
40
- memo: LabelmapMemo;
41
- preview: InitializedOperationData;
27
+ id: string;
28
+ operationType: string;
42
29
  };
43
30
  declare function commitMemo(): boolean;
44
31
  export {};
@@ -1,10 +1,9 @@
1
- import { utilities } from '@cornerstonejs/core';
1
+ import { utilities, eventTarget } from '@cornerstonejs/core';
2
2
  import { triggerSegmentationDataModified } from '../../stateManagement/segmentation/triggerSegmentationEvents';
3
+ import Events from '../../enums/Events';
3
4
  const { VoxelManager, RLEVoxelMap } = utilities;
4
- export function createLabelmapMemo(segmentationId, segmentationVoxelManager, preview) {
5
- return preview
6
- ? createPreviewMemo(segmentationId, preview)
7
- : createRleMemo(segmentationId, segmentationVoxelManager);
5
+ export function createLabelmapMemo(segmentationId, segmentationVoxelManager) {
6
+ return createRleMemo(segmentationId, segmentationVoxelManager);
8
7
  }
9
8
  export function restoreMemo(isUndo) {
10
9
  const { segmentationVoxelManager, undoVoxelManager, redoVoxelManager } = this;
@@ -23,19 +22,8 @@ export function createRleMemo(segmentationId, segmentationVoxelManager) {
23
22
  commitMemo,
24
23
  segmentationVoxelManager,
25
24
  voxelManager,
26
- };
27
- return state;
28
- }
29
- export function createPreviewMemo(segmentationId, preview) {
30
- const { memo: previewMemo, segmentationVoxelManager, previewVoxelManager, } = preview;
31
- const state = {
32
- segmentationId,
33
- restoreMemo,
34
- commitMemo,
35
- segmentationVoxelManager,
36
- voxelManager: previewVoxelManager,
37
- memo: previewMemo,
38
- preview,
25
+ id: utilities.uuidv4(),
26
+ operationType: 'labelmap',
39
27
  };
40
28
  return state;
41
29
  }
@@ -28,11 +28,11 @@ export default class IslandRemoval {
28
28
  ? segmentationVoxels
29
29
  : VoxelManager.createRLEHistoryVoxelManager(segmentationVoxelManager);
30
30
  const { segmentIndex = 1, previewSegmentIndex = 1 } = options;
31
- const clickedPoints = options.points || previewVoxelManager.getPoints();
31
+ const clickedPoints = options.points || segmentationVoxelManager.getPoints();
32
32
  if (!clickedPoints?.length) {
33
33
  return;
34
34
  }
35
- const boundsIJK = previewVoxelManager
35
+ const boundsIJK = segmentationVoxelManager
36
36
  .getBoundsIJK()
37
37
  .map((bound, i) => [
38
38
  Math.min(bound[0], ...clickedPoints.map((point) => point[i])),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "3.7.17",
3
+ "version": "3.8.1",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "types": "./dist/esm/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -103,7 +103,7 @@
103
103
  "canvas": "^3.1.0"
104
104
  },
105
105
  "peerDependencies": {
106
- "@cornerstonejs/core": "^3.7.17",
106
+ "@cornerstonejs/core": "^3.8.1",
107
107
  "@kitware/vtk.js": "32.12.1",
108
108
  "@types/d3-array": "^3.0.4",
109
109
  "@types/d3-interpolate": "^3.0.1",
@@ -122,5 +122,5 @@
122
122
  "type": "individual",
123
123
  "url": "https://ohif.org/donate"
124
124
  },
125
- "gitHead": "9a5dc66176f5172fd3bb11ebcf37f9b8667ab3c4"
125
+ "gitHead": "1f45d352ecb6e239cb418f085cdabeca2a4520a9"
126
126
  }