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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/esm/eventListeners/segmentation/labelmap/onLabelmapSegmentationDataModified.js +27 -23
  2. package/dist/esm/eventListeners/segmentation/segmentationDataModifiedEventListener.js +2 -2
  3. package/dist/esm/stateManagement/segmentation/SegmentationRenderingEngine.d.ts +1 -2
  4. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.d.ts +3 -0
  5. package/dist/esm/stateManagement/segmentation/SegmentationStateManager.js +59 -11
  6. package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.d.ts +1 -0
  7. package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.js +5 -0
  8. package/dist/esm/stateManagement/segmentation/segmentationState.d.ts +2 -1
  9. package/dist/esm/stateManagement/segmentation/segmentationState.js +2 -1
  10. package/dist/esm/tools/segmentation/BrushTool.d.ts +38 -0
  11. package/dist/esm/tools/segmentation/BrushTool.js +40 -7
  12. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +10 -5
  13. package/dist/esm/tools/segmentation/strategies/compositions/islandRemoval.js +1 -1
  14. package/dist/esm/tools/segmentation/strategies/compositions/preview.js +2 -2
  15. package/dist/esm/tools/segmentation/strategies/compositions/setValue.js +6 -4
  16. package/dist/esm/tools/segmentation/strategies/fillSphere.js +5 -14
  17. package/dist/esm/types/LabelmapToolOperationData.d.ts +5 -0
  18. package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +7 -7
  19. package/dist/esm/utilities/segmentation/getSegmentIndexAtLabelmapBorder.js +7 -3
  20. package/dist/esm/utilities/segmentation/isLineInSegment.js +3 -3
  21. package/dist/umd/index.js +1 -1
  22. package/dist/umd/index.js.map +1 -1
  23. package/package.json +3 -3
@@ -1,30 +1,34 @@
1
- import { cache, utilities as csUtils, VolumeViewport, getEnabledElementByViewportId, } from '@cornerstonejs/core';
1
+ import { cache, utilities as csUtils, VolumeViewport, getEnabledElementByViewportId, StackViewport, } from '@cornerstonejs/core';
2
2
  import * as SegmentationState from '../../../stateManagement/segmentation/segmentationState';
3
3
  const onLabelmapSegmentationDataModified = function (evt) {
4
4
  const { segmentationId, modifiedSlicesToUse } = evt.detail;
5
- let modifiedSlices = modifiedSlicesToUse;
6
5
  const { representationData, type } = SegmentationState.getSegmentation(segmentationId);
7
- const labelmapRepresentationData = representationData[type];
8
- if ('stack' in labelmapRepresentationData &&
9
- 'volumeId' in labelmapRepresentationData) {
10
- modifiedSlices = [];
11
- }
12
- if ('volumeId' in labelmapRepresentationData) {
13
- performVolumeLabelmapUpdate({
14
- modifiedSlicesToUse: modifiedSlices,
15
- representationData,
16
- type,
17
- });
18
- }
19
6
  const viewportIds = SegmentationState.getViewportIdsWithSegmentation(segmentationId);
20
- if ('imageIds' in labelmapRepresentationData) {
21
- performStackLabelmapUpdate({
22
- viewportIds,
23
- segmentationId,
24
- representationData,
25
- type,
26
- });
27
- }
7
+ const hasVolumeViewport = viewportIds.some((viewportId) => {
8
+ const { viewport } = getEnabledElementByViewportId(viewportId);
9
+ return viewport instanceof VolumeViewport;
10
+ });
11
+ const hasStackViewport = viewportIds.some((viewportId) => {
12
+ const { viewport } = getEnabledElementByViewportId(viewportId);
13
+ return viewport instanceof StackViewport;
14
+ });
15
+ const hasBothStackAndVolume = hasVolumeViewport && hasStackViewport;
16
+ viewportIds.forEach((viewportId) => {
17
+ const { viewport } = getEnabledElementByViewportId(viewportId);
18
+ if (viewport instanceof VolumeViewport) {
19
+ performVolumeLabelmapUpdate({
20
+ modifiedSlicesToUse: hasBothStackAndVolume ? [] : modifiedSlicesToUse,
21
+ representationData,
22
+ type,
23
+ });
24
+ }
25
+ if (viewport instanceof StackViewport) {
26
+ performStackLabelmapUpdate({
27
+ viewportIds,
28
+ segmentationId,
29
+ });
30
+ }
31
+ });
28
32
  };
29
33
  function performVolumeLabelmapUpdate({ modifiedSlicesToUse, representationData, type, }) {
30
34
  const segmentationVolume = cache.getVolume(representationData[type].volumeId);
@@ -46,7 +50,7 @@ function performVolumeLabelmapUpdate({ modifiedSlicesToUse, representationData,
46
50
  });
47
51
  imageData.modified();
48
52
  }
49
- function performStackLabelmapUpdate({ viewportIds, segmentationId, representationData, type, }) {
53
+ function performStackLabelmapUpdate({ viewportIds, segmentationId }) {
50
54
  viewportIds.forEach((viewportId) => {
51
55
  const viewportSegReps = SegmentationState.getSegmentationRepresentations(viewportId);
52
56
  viewportSegReps.forEach((representation) => {
@@ -1,10 +1,10 @@
1
1
  import SegmentationRepresentations from '../../enums/SegmentationRepresentations';
2
- import * as SegmentationState from '../../stateManagement/segmentation/segmentationState';
3
2
  import { triggerSegmentationRenderBySegmentationId } from '../../stateManagement/segmentation/SegmentationRenderingEngine';
4
3
  import onLabelmapSegmentationDataModified from './labelmap/onLabelmapSegmentationDataModified';
4
+ import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
5
5
  const onSegmentationDataModified = function (evt) {
6
6
  const { segmentationId } = evt.detail;
7
- const { type } = SegmentationState.getSegmentation(segmentationId);
7
+ const { type } = getSegmentation(segmentationId);
8
8
  if (type === SegmentationRepresentations.Labelmap) {
9
9
  onLabelmapSegmentationDataModified(evt);
10
10
  }
@@ -1,4 +1,3 @@
1
- import type { Types } from '@cornerstonejs/core';
2
1
  declare class SegmentationRenderingEngine {
3
2
  private _needsRender;
4
3
  private _animationFrameSet;
@@ -6,7 +5,7 @@ declare class SegmentationRenderingEngine {
6
5
  hasBeenDestroyed: boolean;
7
6
  renderSegmentationsForViewport(viewportId?: string): void;
8
7
  renderSegmentation(segmentationId: string): void;
9
- _getAllViewports: () => Types.IViewport[];
8
+ _getAllViewports: () => import("@cornerstonejs/core").Viewport[];
10
9
  _getViewportIdsForSegmentation(segmentationId?: string): string[];
11
10
  private _throwIfDestroyed;
12
11
  private _setViewportsToBeRenderedNextFrame;
@@ -15,9 +15,12 @@ export default class SegmentationStateManager {
15
15
  getSegmentationRepresentation(segmentationRepresentationUID: string): SegmentationRepresentation | undefined;
16
16
  addSegmentationRepresentationState(segmentationRepresentation: SegmentationRepresentation): void;
17
17
  addSegmentationRepresentationToViewport(viewportId: string, segmentationRepresentationUID: string): void;
18
+ _updateLabelmapSegmentationReferences(segmentationId: any, stackViewport: any, labelmapImageIds: any, updateCallback: any): string;
18
19
  updateLabelmapSegmentationImageReferences(viewportId: any, segmentationId: any): string;
20
+ _updateAllLabelmapSegmentationImageReferences(viewportId: any, segmentationId: any): void;
19
21
  private getLabelmapImageIds;
20
22
  getCurrentLabelmapImageIdForViewport(viewportId: string, segmentationId: string): string | undefined;
23
+ getStackSegmentationImageIdsForViewport(viewportId: string, segmentationId: string): string[];
21
24
  getSegmentationRepresentations(viewportId: string): SegmentationRepresentation[];
22
25
  removeRepresentation(segmentationRepresentationUID: string): void;
23
26
  setActiveSegmentationRepresentation(viewportId: string, segmentationRepresentationUID: string): void;
@@ -111,6 +111,23 @@ export default class SegmentationStateManager {
111
111
  }
112
112
  this.setActiveSegmentationRepresentation(viewportId, segmentationRepresentationUID);
113
113
  }
114
+ _updateLabelmapSegmentationReferences(segmentationId, stackViewport, labelmapImageIds, updateCallback) {
115
+ const currentImageId = stackViewport.getCurrentImageId();
116
+ for (const labelmapImageId of labelmapImageIds) {
117
+ const viewableImageId = stackViewport.isReferenceViewable({ referencedImageId: labelmapImageId }, { asOverlay: true });
118
+ if (viewableImageId) {
119
+ this._stackLabelmapImageIdReferenceMap
120
+ .get(segmentationId)
121
+ .set(currentImageId, labelmapImageId);
122
+ }
123
+ }
124
+ if (updateCallback) {
125
+ updateCallback(stackViewport, segmentationId, labelmapImageIds);
126
+ }
127
+ return this._stackLabelmapImageIdReferenceMap
128
+ .get(segmentationId)
129
+ .get(currentImageId);
130
+ }
114
131
  updateLabelmapSegmentationImageReferences(viewportId, segmentationId) {
115
132
  const segmentation = this.getSegmentation(segmentationId);
116
133
  if (!segmentation) {
@@ -126,18 +143,36 @@ export default class SegmentationStateManager {
126
143
  const labelmapImageIds = this.getLabelmapImageIds(representationData);
127
144
  const enabledElement = getEnabledElementByViewportId(viewportId);
128
145
  const stackViewport = enabledElement.viewport;
129
- const currentImageId = stackViewport.getCurrentImageId();
130
- for (const labelmapImageId of labelmapImageIds) {
131
- const viewableImageId = stackViewport.isReferenceViewable({ referencedImageId: labelmapImageId }, { asOverlay: true });
132
- if (viewableImageId) {
133
- this._stackLabelmapImageIdReferenceMap
134
- .get(segmentationId)
135
- .set(currentImageId, labelmapImageId);
136
- }
146
+ return this._updateLabelmapSegmentationReferences(segmentationId, stackViewport, labelmapImageIds, null);
147
+ }
148
+ _updateAllLabelmapSegmentationImageReferences(viewportId, segmentationId) {
149
+ const segmentation = this.getSegmentation(segmentationId);
150
+ if (!segmentation) {
151
+ return;
137
152
  }
138
- return this._stackLabelmapImageIdReferenceMap
139
- .get(segmentationId)
140
- .get(currentImageId);
153
+ if (!this._stackLabelmapImageIdReferenceMap.has(segmentationId)) {
154
+ this._stackLabelmapImageIdReferenceMap.set(segmentationId, new Map());
155
+ }
156
+ const { representationData } = segmentation;
157
+ if (!representationData.LABELMAP) {
158
+ return;
159
+ }
160
+ const labelmapImageIds = this.getLabelmapImageIds(representationData);
161
+ const enabledElement = getEnabledElementByViewportId(viewportId);
162
+ const stackViewport = enabledElement.viewport;
163
+ this._updateLabelmapSegmentationReferences(segmentationId, stackViewport, labelmapImageIds, (stackViewport, segmentationId, labelmapImageIds) => {
164
+ const imageIds = stackViewport.getImageIds();
165
+ imageIds.forEach((imageId, index) => {
166
+ for (const labelmapImageId of labelmapImageIds) {
167
+ const viewableImageId = stackViewport.isReferenceViewable({ referencedImageId: labelmapImageId, sliceIndex: index }, { asOverlay: true, withNavigation: true });
168
+ if (viewableImageId) {
169
+ this._stackLabelmapImageIdReferenceMap
170
+ .get(segmentationId)
171
+ .set(imageId, labelmapImageId);
172
+ }
173
+ }
174
+ });
175
+ });
141
176
  }
142
177
  getLabelmapImageIds(representationData) {
143
178
  const labelmapData = representationData.LABELMAP;
@@ -168,6 +203,19 @@ export default class SegmentationStateManager {
168
203
  const imageIdReferenceMap = this._stackLabelmapImageIdReferenceMap.get(segmentationId);
169
204
  return imageIdReferenceMap.get(currentImageId);
170
205
  }
206
+ getStackSegmentationImageIdsForViewport(viewportId, segmentationId) {
207
+ const segmentation = this.getSegmentation(segmentationId);
208
+ if (!segmentation) {
209
+ return [];
210
+ }
211
+ this._updateAllLabelmapSegmentationImageReferences(viewportId, segmentationId);
212
+ const { viewport } = getEnabledElementByViewportId(viewportId);
213
+ const imageIds = viewport.getImageIds();
214
+ const associatedReferenceImageAndLabelmapImageIds = this._stackLabelmapImageIdReferenceMap.get(segmentationId);
215
+ return imageIds.map((imageId) => {
216
+ return associatedReferenceImageAndLabelmapImageIds.get(imageId);
217
+ });
218
+ }
171
219
  getSegmentationRepresentations(viewportId) {
172
220
  const viewport = this.state.viewports[viewportId];
173
221
  if (!viewport) {
@@ -0,0 +1 @@
1
+ export declare function getStackSegmentationImageIdsForViewport(viewportId: string, segmentationId: string): string[];
@@ -0,0 +1,5 @@
1
+ import { defaultSegmentationStateManager } from './SegmentationStateManager';
2
+ export function getStackSegmentationImageIdsForViewport(viewportId, segmentationId) {
3
+ const segmentationStateManager = defaultSegmentationStateManager;
4
+ return segmentationStateManager.getStackSegmentationImageIdsForViewport(viewportId, segmentationId);
5
+ }
@@ -26,4 +26,5 @@ import { getActiveSegmentationRepresentation } from './getActiveSegmentationRepr
26
26
  import { setActiveSegmentationRepresentation } from './setActiveSegmentationRepresentation';
27
27
  import { getCurrentLabelmapImageIdForViewport } from './getCurrentLabelmapImageIdForViewport';
28
28
  import { updateLabelmapSegmentationImageReferences } from './updateLabelmapSegmentationImageReferences';
29
- export { getSegmentation, getSegmentations, addSegmentation, removeSegmentation, getAllSegmentationRepresentations, getSegmentationRepresentation, removeRepresentation, getGlobalConfig, setGlobalConfig, getSegmentationRepresentationConfig, setSegmentationRepresentationConfig, getPerSegmentConfig, setPerSegmentConfig, getSegmentationRepresentations, addSegmentationRepresentation, getSegmentationRepresentationViewportStates, addColorLUT, getColorLUT, getNextColorLUTIndex, removeColorLUT, getSegmentationRepresentationsForSegmentation, getSegmentationRepresentationVisibility, setSegmentationRepresentationVisibility, getViewportIdsWithSegmentation, getActiveSegmentationRepresentation, setActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, updateLabelmapSegmentationImageReferences, };
29
+ import { getStackSegmentationImageIdsForViewport } from './getStackSegmentationImageIdsForViewport';
30
+ export { getSegmentation, getSegmentations, addSegmentation, removeSegmentation, getAllSegmentationRepresentations, getSegmentationRepresentation, removeRepresentation, getGlobalConfig, setGlobalConfig, getSegmentationRepresentationConfig, setSegmentationRepresentationConfig, getPerSegmentConfig, setPerSegmentConfig, getSegmentationRepresentations, addSegmentationRepresentation, getSegmentationRepresentationViewportStates, addColorLUT, getColorLUT, getNextColorLUTIndex, removeColorLUT, getSegmentationRepresentationsForSegmentation, getSegmentationRepresentationVisibility, setSegmentationRepresentationVisibility, getViewportIdsWithSegmentation, getActiveSegmentationRepresentation, setActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, updateLabelmapSegmentationImageReferences, getStackSegmentationImageIdsForViewport, };
@@ -26,4 +26,5 @@ import { getActiveSegmentationRepresentation } from './getActiveSegmentationRepr
26
26
  import { setActiveSegmentationRepresentation } from './setActiveSegmentationRepresentation';
27
27
  import { getCurrentLabelmapImageIdForViewport } from './getCurrentLabelmapImageIdForViewport';
28
28
  import { updateLabelmapSegmentationImageReferences } from './updateLabelmapSegmentationImageReferences';
29
- export { getSegmentation, getSegmentations, addSegmentation, removeSegmentation, getAllSegmentationRepresentations, getSegmentationRepresentation, removeRepresentation, getGlobalConfig, setGlobalConfig, getSegmentationRepresentationConfig, setSegmentationRepresentationConfig, getPerSegmentConfig, setPerSegmentConfig, getSegmentationRepresentations, addSegmentationRepresentation, getSegmentationRepresentationViewportStates, addColorLUT, getColorLUT, getNextColorLUTIndex, removeColorLUT, getSegmentationRepresentationsForSegmentation, getSegmentationRepresentationVisibility, setSegmentationRepresentationVisibility, getViewportIdsWithSegmentation, getActiveSegmentationRepresentation, setActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, updateLabelmapSegmentationImageReferences, };
29
+ import { getStackSegmentationImageIdsForViewport } from './getStackSegmentationImageIdsForViewport';
30
+ export { getSegmentation, getSegmentations, addSegmentation, removeSegmentation, getAllSegmentationRepresentations, getSegmentationRepresentation, removeRepresentation, getGlobalConfig, setGlobalConfig, getSegmentationRepresentationConfig, setSegmentationRepresentationConfig, getPerSegmentConfig, setPerSegmentConfig, getSegmentationRepresentations, addSegmentationRepresentation, getSegmentationRepresentationViewportStates, addColorLUT, getColorLUT, getNextColorLUTIndex, removeColorLUT, getSegmentationRepresentationsForSegmentation, getSegmentationRepresentationVisibility, setSegmentationRepresentationVisibility, getViewportIdsWithSegmentation, getActiveSegmentationRepresentation, setActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, updateLabelmapSegmentationImageReferences, getStackSegmentationImageIdsForViewport, };
@@ -1,6 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import type { PublicToolProps, ToolProps, EventTypes, SVGDrawingHelper } from '../../types';
3
3
  import { BaseTool } from '../base';
4
+ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
4
5
  export type PreviewData = {
5
6
  preview: unknown;
6
7
  timer?: number;
@@ -24,11 +25,22 @@ declare class BrushTool extends BaseTool {
24
25
  referencedVolumeId: any;
25
26
  segmentsLocked: number[] | [];
26
27
  imageId?: undefined;
28
+ override?: undefined;
27
29
  } | {
28
30
  imageId: string;
29
31
  segmentsLocked: number[] | [];
30
32
  volumeId?: undefined;
31
33
  referencedVolumeId?: undefined;
34
+ override?: undefined;
35
+ } | {
36
+ imageId: string;
37
+ segmentsLocked: number[] | [];
38
+ override: {
39
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
40
+ imageData: vtkImageData;
41
+ };
42
+ volumeId?: undefined;
43
+ referencedVolumeId?: undefined;
32
44
  };
33
45
  preMouseDownCallback: (evt: EventTypes.MouseDownActivateEventType) => boolean;
34
46
  mouseMoveCallback: (evt: EventTypes.InteractionEventType) => void;
@@ -48,8 +60,13 @@ declare class BrushTool extends BaseTool {
48
60
  viewUp: any;
49
61
  strategySpecificConfiguration: any;
50
62
  preview: unknown;
63
+ override: {
64
+ voxelManager: Types.IVoxelManager<number>;
65
+ imageData: vtkImageData;
66
+ };
51
67
  segmentsLocked: number[];
52
68
  imageId?: string;
69
+ imageIds?: string[];
53
70
  volumeId?: string;
54
71
  referencedVolumeId?: string;
55
72
  } | {
@@ -67,6 +84,23 @@ declare class BrushTool extends BaseTool {
67
84
  referencedVolumeId: any;
68
85
  segmentsLocked: number[] | [];
69
86
  imageId?: undefined;
87
+ override?: undefined;
88
+ } | {
89
+ points: any;
90
+ segmentIndex: number;
91
+ previewColors: any;
92
+ viewPlaneNormal: any;
93
+ toolGroupId: string;
94
+ segmentationId: string;
95
+ segmentationRepresentationUID: string;
96
+ viewUp: any;
97
+ strategySpecificConfiguration: any;
98
+ preview: unknown;
99
+ imageId: string;
100
+ segmentsLocked: number[] | [];
101
+ volumeId?: undefined;
102
+ referencedVolumeId?: undefined;
103
+ override?: undefined;
70
104
  } | {
71
105
  points: any;
72
106
  segmentIndex: number;
@@ -80,6 +114,10 @@ declare class BrushTool extends BaseTool {
80
114
  preview: unknown;
81
115
  imageId: string;
82
116
  segmentsLocked: number[] | [];
117
+ override: {
118
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
119
+ imageData: vtkImageData;
120
+ };
83
121
  volumeId?: undefined;
84
122
  referencedVolumeId?: undefined;
85
123
  };
@@ -10,10 +10,11 @@ import { drawCircle as drawCircleSvg } from '../../drawingSvg';
10
10
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
11
11
  import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
12
12
  import { isVolumeSegmentation } from './strategies/utils/stackVolumeCheck';
13
- import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, } from '../../stateManagement/segmentation/segmentationState';
13
+ import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, getStackSegmentationImageIdsForViewport, } from '../../stateManagement/segmentation/segmentationState';
14
14
  import { getLockedSegmentIndices } from '../../stateManagement/segmentation/segmentLocking';
15
15
  import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
16
16
  import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
17
+ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
17
18
  class BrushTool extends BaseTool {
18
19
  constructor(toolProps = {}, defaultToolProps = {
19
20
  supportedInteractionTypes: ['Mouse', 'Touch'],
@@ -141,7 +142,6 @@ class BrushTool extends BaseTool {
141
142
  const eventData = evt.detail;
142
143
  const { element, currentPoints } = eventData;
143
144
  const enabledElement = getEnabledElement(element);
144
- const { renderingEngine } = enabledElement;
145
145
  this.updateCursor(evt);
146
146
  const { viewportIdsToRender } = this._hoverData;
147
147
  triggerAnnotationRenderForViewportUIDs(viewportIdsToRender);
@@ -235,12 +235,45 @@ class BrushTool extends BaseTool {
235
235
  return;
236
236
  }
237
237
  if (this.configuration.activeStrategy.includes('SPHERE')) {
238
- throw new Error('Sphere manipulation is not supported for stacks of image segmentations yet');
238
+ const referencedImageIds = viewport.getImageIds();
239
+ const isValidVolumeForSphere = csUtils.isValidVolume(referencedImageIds);
240
+ if (!isValidVolumeForSphere) {
241
+ throw new Error('Volume is not reconstructable for sphere manipulation');
242
+ }
243
+ const labelmapImageIds = getStackSegmentationImageIdsForViewport(viewport.id, segmentationId);
244
+ if (!labelmapImageIds || labelmapImageIds.length === 1) {
245
+ return {
246
+ imageId: segmentationImageId,
247
+ segmentsLocked,
248
+ };
249
+ }
250
+ const tempVolumeId = 'tempVolumeId';
251
+ const { dimensions, direction, origin, spacing, numberOfComponents, imageIds: sortedLabelmapImageIds, } = csUtils.generateVolumePropsFromImageIds(labelmapImageIds, tempVolumeId);
252
+ const newVoxelManager = csUtils.VoxelManager.createImageVolumeVoxelManager({
253
+ dimensions,
254
+ imageIds: sortedLabelmapImageIds,
255
+ numberOfComponents,
256
+ });
257
+ const newImageData = vtkImageData.newInstance();
258
+ newImageData.setDimensions(dimensions);
259
+ newImageData.setSpacing(spacing);
260
+ newImageData.setDirection(direction);
261
+ newImageData.setOrigin(origin);
262
+ return {
263
+ imageId: segmentationImageId,
264
+ segmentsLocked,
265
+ override: {
266
+ voxelManager: newVoxelManager,
267
+ imageData: newImageData,
268
+ },
269
+ };
270
+ }
271
+ else {
272
+ return {
273
+ imageId: segmentationImageId,
274
+ segmentsLocked,
275
+ };
239
276
  }
240
- return {
241
- imageId: segmentationImageId,
242
- segmentsLocked,
243
- };
244
277
  }
245
278
  }
246
279
  createHoverData(element, centerCanvas) {
@@ -37,7 +37,8 @@ export default class BrushStrategy {
37
37
  }
38
38
  this._fill.forEach((func) => func(initializedData));
39
39
  const { segmentationVoxelManager, previewVoxelManager, previewSegmentIndex, } = initializedData;
40
- triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfSlices());
40
+ triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices());
41
+ segmentationVoxelManager.resetModifiedSlices();
41
42
  if (!previewSegmentIndex || !previewVoxelManager.modifiedSlices.size) {
42
43
  return null;
43
44
  }
@@ -69,7 +70,9 @@ export default class BrushStrategy {
69
70
  BrushStrategy.childFunctions[key](this, result[key]);
70
71
  }
71
72
  });
72
- this.strategyFunction = (enabledElement, operationData) => this.fill(enabledElement, operationData);
73
+ this.strategyFunction = (enabledElement, operationData) => {
74
+ return this.fill(enabledElement, operationData);
75
+ };
73
76
  for (const key of Object.keys(BrushStrategy.childFunctions)) {
74
77
  this.strategyFunction[key] = this[key];
75
78
  }
@@ -82,9 +85,11 @@ export default class BrushStrategy {
82
85
  return operationData.preview;
83
86
  }
84
87
  const { imageVoxelManager, segmentationVoxelManager, segmentationImageData, } = data;
88
+ const segmentationVoxelManagerToUse = operationData.override?.voxelManager || segmentationVoxelManager;
89
+ const segmentationImageDataToUse = operationData.override?.imageData || segmentationImageData;
85
90
  const previewVoxelManager = operationData.preview?.previewVoxelManager ||
86
91
  VoxelManager.createHistoryVoxelManager({
87
- sourceVoxelManager: segmentationVoxelManager,
92
+ sourceVoxelManager: segmentationVoxelManagerToUse,
88
93
  });
89
94
  const previewEnabled = !!operationData.previewColors;
90
95
  const previewSegmentIndex = previewEnabled ? 255 : undefined;
@@ -94,8 +99,8 @@ export default class BrushStrategy {
94
99
  ...operationData,
95
100
  enabledElement,
96
101
  imageVoxelManager,
97
- segmentationVoxelManager,
98
- segmentationImageData,
102
+ segmentationVoxelManager: segmentationVoxelManagerToUse,
103
+ segmentationImageData: segmentationImageDataToUse,
99
104
  previewVoxelManager,
100
105
  viewport,
101
106
  centerWorld: null,
@@ -118,6 +118,6 @@ export default {
118
118
  }
119
119
  }
120
120
  }
121
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfSlices());
121
+ triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
122
122
  },
123
123
  };
@@ -66,7 +66,7 @@ export default {
66
66
  }
67
67
  };
68
68
  tracking.forEach(callback, {});
69
- triggerSegmentationDataModified(operationData.segmentationId, tracking.getArrayOfSlices());
69
+ triggerSegmentationDataModified(operationData.segmentationId, tracking.getArrayOfModifiedSlices());
70
70
  tracking.clear();
71
71
  },
72
72
  [StrategyCallbacks.RejectPreview]: (operationData) => {
@@ -78,7 +78,7 @@ export default {
78
78
  segmentationVoxelManager.setAtIndex(index, value);
79
79
  };
80
80
  previewVoxelManager.forEach(callback);
81
- triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfSlices());
81
+ triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
82
82
  previewVoxelManager.clear();
83
83
  },
84
84
  };
@@ -1,12 +1,14 @@
1
1
  import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
2
+ import { triggerEvent, eventTarget } from '@cornerstonejs/core';
2
3
  export default {
3
4
  [StrategyCallbacks.INTERNAL_setValue]: (operationData, { value, index }) => {
4
- const { segmentsLocked, segmentIndex, previewVoxelManager, previewSegmentIndex, segmentationVoxelManager, } = operationData;
5
+ const { segmentsLocked, segmentIndex, previewVoxelManager, previewSegmentIndex, segmentationVoxelManager, segmentationId, } = operationData;
5
6
  const existingValue = segmentationVoxelManager.getAtIndex(index);
7
+ let changed = false;
6
8
  if (segmentIndex === null) {
7
9
  const oldValue = previewVoxelManager.getAtIndex(index);
8
10
  if (oldValue !== undefined) {
9
- previewVoxelManager.setAtIndex(index, oldValue);
11
+ changed = previewVoxelManager.setAtIndex(index, oldValue);
10
12
  }
11
13
  return;
12
14
  }
@@ -15,13 +17,13 @@ export default {
15
17
  }
16
18
  if (existingValue === previewSegmentIndex) {
17
19
  if (previewVoxelManager.getAtIndex(index) === undefined) {
18
- segmentationVoxelManager.setAtIndex(index, segmentIndex);
20
+ changed = segmentationVoxelManager.setAtIndex(index, segmentIndex);
19
21
  }
20
22
  else {
21
23
  return;
22
24
  }
23
25
  }
24
26
  const useSegmentIndex = previewSegmentIndex ?? segmentIndex;
25
- previewVoxelManager.setAtIndex(index, useSegmentIndex);
27
+ changed = previewVoxelManager.setAtIndex(index, useSegmentIndex);
26
28
  },
27
29
  };
@@ -21,20 +21,11 @@ const sphereComposition = {
21
21
  operationData.centerIJK = transformWorldToIndex(segmentationImageData, center);
22
22
  const { boundsIJK: newBoundsIJK, topLeftWorld, bottomRightWorld, } = getSphereBoundsInfo(points.slice(0, 2), segmentationImageData, viewport);
23
23
  segmentationVoxelManager.boundsIJK = newBoundsIJK;
24
- if (imageVoxelManager) {
25
- imageVoxelManager.isInObject = createEllipseInPoint({
26
- topLeftWorld,
27
- bottomRightWorld,
28
- center,
29
- });
30
- }
31
- else {
32
- segmentationVoxelManager.isInObject = createEllipseInPoint({
33
- topLeftWorld,
34
- bottomRightWorld,
35
- center,
36
- });
37
- }
24
+ segmentationVoxelManager.isInObject = createEllipseInPoint({
25
+ topLeftWorld,
26
+ bottomRightWorld,
27
+ center,
28
+ });
38
29
  },
39
30
  };
40
31
  const SPHERE_STRATEGY = new BrushStrategy('Sphere', compositions.regionFill, compositions.setValue, sphereComposition, compositions.determineSegmentIndex, compositions.preview);
@@ -1,5 +1,6 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import type { LabelmapSegmentationDataStack, LabelmapSegmentationDataVolume } from './LabelmapTypes';
3
+ import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3
4
  type LabelmapToolOperationData = {
4
5
  segmentationId: string;
5
6
  segmentIndex: number;
@@ -11,6 +12,10 @@ type LabelmapToolOperationData = {
11
12
  segmentationRepresentationUID: string;
12
13
  points: Types.Point3[];
13
14
  voxelManager: any;
15
+ override: {
16
+ voxelManager: Types.IVoxelManager<number>;
17
+ imageData: vtkImageData;
18
+ };
14
19
  preview: any;
15
20
  toolGroupId: string;
16
21
  };
@@ -15,13 +15,13 @@ function generateContourSetsFromLabelmap({ segmentations }) {
15
15
  return;
16
16
  }
17
17
  const numSlices = vol.dimensions[2];
18
- const segData = vol.imageData.getPointData().getScalars().getData();
18
+ const voxelManager = vol.voxelManager;
19
19
  const pixelsPerSlice = vol.dimensions[0] * vol.dimensions[1];
20
20
  for (let z = 0; z < numSlices; z++) {
21
21
  for (let y = 0; y < vol.dimensions[1]; y++) {
22
22
  const index = y * vol.dimensions[0] + z * pixelsPerSlice;
23
- segData[index] = 0;
24
- segData[index + vol.dimensions[0] - 1] = 0;
23
+ voxelManager.setAtIndex(index, 0);
24
+ voxelManager.setAtIndex(index + vol.dimensions[0] - 1, 0);
25
25
  }
26
26
  }
27
27
  const ContourSets = [];
@@ -41,13 +41,13 @@ function generateContourSetsFromLabelmap({ segmentations }) {
41
41
  });
42
42
  const { containedSegmentIndices } = segment;
43
43
  for (let sliceIndex = 0; sliceIndex < numSlices; sliceIndex++) {
44
- if (isSliceEmptyForSegment(sliceIndex, segData, pixelsPerSlice, segIndex)) {
44
+ if (isSliceEmptyForSegment(sliceIndex, voxelManager, pixelsPerSlice, segIndex)) {
45
45
  continue;
46
46
  }
47
47
  const frameStart = sliceIndex * pixelsPerSlice;
48
48
  try {
49
49
  for (let i = 0; i < pixelsPerSlice; i++) {
50
- const value = segData[i + frameStart];
50
+ const value = voxelManager.getAtIndex(i + frameStart);
51
51
  if (value === segIndex || containedSegmentIndices?.has(value)) {
52
52
  scalars.setValue(i + frameStart, 1);
53
53
  }
@@ -96,11 +96,11 @@ function generateContourSetsFromLabelmap({ segmentations }) {
96
96
  }
97
97
  return ContourSets;
98
98
  }
99
- function isSliceEmptyForSegment(sliceIndex, segData, pixelsPerSlice, segIndex) {
99
+ function isSliceEmptyForSegment(sliceIndex, voxelManager, pixelsPerSlice, segIndex) {
100
100
  const startIdx = sliceIndex * pixelsPerSlice;
101
101
  const endIdx = startIdx + pixelsPerSlice;
102
102
  for (let i = startIdx; i < endIdx; i++) {
103
- if (segData[i] === segIndex) {
103
+ if (voxelManager.getAtIndex(i) === segIndex) {
104
104
  return false;
105
105
  }
106
106
  }
@@ -4,14 +4,16 @@ import { isVolumeSegmentation } from '../../tools/segmentation/strategies/utils/
4
4
  export function getSegmentIndexAtLabelmapBorder(segmentationId, worldPoint, { viewport, searchRadius }) {
5
5
  const segmentation = getSegmentation(segmentationId);
6
6
  const labelmapData = segmentation.representationData.LABELMAP;
7
- if (isVolumeSegmentation(labelmapData)) {
7
+ if (isVolumeSegmentation(labelmapData, viewport)) {
8
8
  const { volumeId } = labelmapData;
9
9
  const segmentationVolume = cache.getVolume(volumeId);
10
10
  if (!segmentationVolume) {
11
11
  return;
12
12
  }
13
+ const voxelManager = segmentationVolume.voxelManager;
13
14
  const imageData = segmentationVolume.imageData;
14
- const segmentIndex = imageData.getScalarValueFromWorld(worldPoint);
15
+ const indexIJK = utilities.transformWorldToIndex(imageData, worldPoint);
16
+ const segmentIndex = voxelManager.getAtIJK(indexIJK[0], indexIJK[1], indexIJK[2]);
15
17
  const canvasPoint = viewport.worldToCanvas(worldPoint);
16
18
  const onEdge = isSegmentOnEdgeCanvas(canvasPoint, segmentIndex, viewport, imageData, searchRadius);
17
19
  return onEdge ? segmentIndex : undefined;
@@ -69,7 +71,9 @@ function isSegmentOnEdgeCanvas(canvasPoint, segmentIndex, viewport, imageData, s
69
71
  const getNeighborIndex = (deltaI, deltaJ) => {
70
72
  const neighborCanvas = [canvasPoint[0] + deltaI, canvasPoint[1] + deltaJ];
71
73
  const worldPoint = viewport.canvasToWorld(neighborCanvas);
72
- return imageData.getScalarValueFromWorld(worldPoint);
74
+ const voxelManager = imageData.get('voxelManager').voxelManager;
75
+ const indexIJK = utilities.transformWorldToIndex(imageData, worldPoint);
76
+ return voxelManager.getAtIJK(indexIJK[0], indexIJK[1], indexIJK[2]);
73
77
  };
74
78
  return isSegmentOnEdge(getNeighborIndex, segmentIndex, searchRadius);
75
79
  }
@@ -25,7 +25,7 @@ function createIsInSegment(segVolumeId, segmentIndex, containedSegmentIndices) {
25
25
  console.warn(`No volume found for ${segVolumeId}`);
26
26
  return;
27
27
  }
28
- const segData = vol.imageData.getPointData().getScalars().getData();
28
+ const voxelManager = vol.voxelManager;
29
29
  const width = vol.dimensions[0];
30
30
  const pixelsPerSlice = width * vol.dimensions[1];
31
31
  return {
@@ -34,14 +34,14 @@ function createIsInSegment(segVolumeId, segmentIndex, containedSegmentIndices) {
34
34
  const ijk = vol.imageData.worldToIndex(point).map(Math.round);
35
35
  const [i, j, k] = ijk;
36
36
  const index = i + j * width + k * pixelsPerSlice;
37
- const value = segData[index];
37
+ const value = voxelManager.getAtIndex(index);
38
38
  return value === segmentIndex || containedSegmentIndices?.has(value);
39
39
  },
40
40
  toIJK: (point) => vol.imageData.worldToIndex(point),
41
41
  testIJK: (ijk) => {
42
42
  const [i, j, k] = ijk;
43
43
  const index = Math.round(i) + Math.round(j) * width + Math.round(k) * pixelsPerSlice;
44
- const value = segData[index];
44
+ const value = voxelManager.getAtIndex(index);
45
45
  return value === segmentIndex || containedSegmentIndices?.has(value);
46
46
  },
47
47
  };